什么时候写"ad hoc sql"vs存储过程更好

Luk*_*101 37 c# sql sql-server-2008

我的应用程序中有100%的临时sql.我的朋友建议我转换为存储过程以获得额外的性能和安全性.这提出了一个问题,除了速度和安全性之外还有其他任何理由坚持使用ad hoc sql查询吗?

Mus*_*sis 71

SQL Server缓存临时查询的执行计划,因此(折扣第一次调用所花费的时间)这两种方法在速度方面是相同的.

通常,使用存储过程意味着获取应用程序所需的部分代码(T-SQL查询)并将其放在不受源代码控制的位置(它可以,但通常不是)和在你不知情的情况下被别人改变的地方.

将查询放在这样的中心位置可能是件好事,这取决于有多少不同的应用程序需要访问它们所代表的数据.我通常发现保持应用程序使用的查询驻留在应用程序代码本身更容易.

在20世纪90年代中期,传统观点认为SQL Server中的存储过程是在性能关键的情况下发展的方式,当时它们肯定是.然而,这个CW背后的原因已经很长时间没有了.

更新: 此外,经常在讨论存储过程的可行性时,需要防止SQL注入以保护过程.当然,没有一个心智正常的人认为通过字符串连接组装即席查询是正确的事情(尽管如果你连接用户输入,这只会让你接受SQL注入攻击).显然,临时查询应该被参数化,不仅是为了防止sql注入攻击下的怪物,而且只是为了让你作为程序员的生活变得更加容易(除非你喜欢弄清楚何时使用单个引用你的价值观).

更新2: 我做了更多的研究.根据这篇MSDN白皮书,答案似乎取决于您对查询的"ad-hoc"的含义.例如,一个简单的查询,如下所示:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5
Run Code Online (Sandbox Code Playgroud)

... 缓存其执行计划.此外,因为查询不包含某些不合格元素(几乎除了一个表中的简单SELECT之外的其他任何元素),SQL Server实际上将"自动参数化"查询并用参数替换文字常量"5",并缓存参数化版本的执行计划.这意味着如果您随后执行即席查询:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23
Run Code Online (Sandbox Code Playgroud)

...它将能够使用缓存的执行计划.

不幸的是,不合格查询元素的自动参数的名单很长(例如,忘记使用DISTINCT,TOP,UNION,GROUP BY,OR等),所以你真的不能表现这个数.

如果您确实有一个不会自动参数化的"超级复杂"查询,例如:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23
Run Code Online (Sandbox Code Playgroud)

...它仍然会被查询的确切文本缓存,因此如果您的应用程序重复使用相同的文字"硬编码"值调用此查询,则第一个查询之后的每个查询将重新使用缓存的执行计划(和因此,与存储过程一样快.

如果文字值发生变化(例如,基于用户操作,如过滤或排序已查看的数据),则查询将无法从缓存中受益(除非偶尔意外地与最近的查询完全匹配).

通过"ad-hoc"查询进行缓存的方法是参数化它们.在C#中动态创建查询,如下所示:

int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();
Run Code Online (Sandbox Code Playgroud)

是不正确的.正确的方法(使用ADO.Net)将是这样的:

using (SqlConnection conn = new SqlConnection(connStr))
{
    SqlCommand com = new SqlCommand(conn);
    com.CommandType = CommandType.Text;
    com.CommandText = 
        "DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
    int itemCount = 5;
    com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
    com.Prepare();
    com.ExecuteNonQuery();
}
Run Code Online (Sandbox Code Playgroud)

查询不包含文字,并且已经完全参数化,因此使用相同参数化语句的后续查询将使用缓存计划(即使使用不同的参数值调用).请注意,这里的代码实际上与用于调用存储过程的代码相同(唯一的区别是CommandType和CommandText),因此它有点归结为您希望该查询的文本"活"的位置"(在您的应用程序代码或存储过程中).

最后,如果通过"ad-hoc"查询,你的意思是你动态构建具有不同列,表,过滤参数等等的查询,就像这些:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
    ORDER BY LASTNAME DESC
Run Code Online (Sandbox Code Playgroud)

...那么你几乎不能用存储过程做到这一点(没有EXEC礼貌社会中没有提到的hack),所以重点是没有实际意义.

更新3: 这是使用存储过程的唯一非常好的与性能相关的原因(无论如何我都能想到).如果您的查询是一个长期运行的查询,其中编译执行计划的过程比实际执行花费的时间要长得多,并且查询只是不经常调用(例如,像月度报告),那么将它放在存储过程中可能使SQL Server将已编译的计划保留在缓存中足够长的时间,使其仍然在下个月左右.但是,如果这是真的与否,请打败我.

  • 我在这个网站上看到的最好的答案之一. (2认同)

Bil*_*win 19

没有任何关于存储过程使它们神奇地更快或更安全.有些情况下,精心设计的存储过程可以更快地执行某些类型的任务,但对于临时SQL也是如此.

按照您发现最有效率的方式编码.

"在你加快速度之前做好准备." - Brian Kernighan

  • @Dave:缓存ad-hoc SQL和存储过程的编译计划.第一次调用过程与第一次调用ad-hoc SQL一样慢.程序计划更为笼统; 但比如说,根据具体情况,这可能是好事也可能是坏事 (6认同)
  • 我的观点是,人们不能声称使用存储过程作为一般规则更好或更差.无论如何都有很多例外情况,所以我们必须接受没有银弹. (3认同)

Eri*_* J. 12

如果您不编写存储过程,请调查参数化查询.如果您自己构建SQL(包括参数连接),则会引发SQL注入攻击.

  • lb:我听说对于真正的必杀技,你必须创建一个SOAP服务来提供XML以及对象关系映射器,这样你就可以用XPath或更好的XQuery查询应用程序中的XML.但我也听说在彩虹结束时,你可能会找到一罐金子,想到它,我实际上从来没有找到过:(( (5认同)

Dan*_*ger 9

有一些与此主题相关的神话你应该自己解除:

误区1:存储过程是预先编译的
http://scarydba.wordpress.com/2009/09/30/pre-compiled-stored-procedures-fact-or-myth/

误解2:Ad Hoc SQL查询不重用执行计划: http ://scarydba.wordpress.com/2009/10/05/ad-hoc-queries-dont-reuse-execution-plans-myth-or-fact/

当你绝对需要锁定数据库时,恕我直言的过程有优势.在这些情况下,您可以使用仅具有执行存储过程权限的帐户.此外,它们可以从DBA角度在您的应用程序和数据库之间提供一个抽象层.

同样,动态SQL在查询可能需要更改某些内容并且......好......动态的情况下更好.或者,如果您知道必须移植到多个数据库.

只要所有用户输入的值都参数化,两者在SQL注入方面都是安全的.

  • 如果您仍然使用15年前发布的数据库平台,那么关于存储过程与即席查询的争论是您遇到的最少问题. (6认同)
  • @Dave:你真的读过那篇文章吗?您引用的文本是指v6,一个古老版本的SQL Server.对于较新的版本:"SQL Server 2000和SQL Server 7.0在创建存储过程时不保存部分编译的计划.存储过程在执行时编译,就像任何其他Transact-SQL语句一样." (3认同)

Rol*_*man 5

已经有很多关于该线程的性能,缓存和安全性的文章,我不再赘述。在此线程中,我还没有读过几件事,即可移植性问题和绕行旅行。

  • 如果您对跨编程语言的应用程序的最大可移植性感兴趣,那么存储过程是一个好主意:存储在应用程序外部数据库中的程序逻辑越多,如果要转移到其他框架或语言。另外,用于调用存储过程的代码比实际的原始SQL本身要小得多,因此应用程序代码中的数据库接口的占用空间将较小。
  • 如果在多个应用程序中需要相同的逻辑,则存储过程很方便,因为可以对该逻辑进行单个定义,而其他应用程序可以重复使用该逻辑。但是,这种好处经常被夸大,因为您还可以在跨应用程序共享的库中隔离该逻辑。当然,如果应用程序使用不同的语言,则存储过程确实有好处,因为通过语言的db接口调用过程可能比链接到用另一种语言编写的库更容易。
  • 如果您对RDBMS可移植性感兴趣,那么存储过程可能会成为您最大的问题之一。所有主要和次要RDBMS的核心功能都非常相似。可以在语法和存储过程的可用内置功能中找到最大的差异。

关于往返:

  • 如果您的应用程序中有许多多语句事务,或者通常是需要多个SQL语句的函数,那么如果将这些多个语句放在存储过程中,则性能会提高。原因是调用存储过程(并可能从中返回多个结果)只是一个单程行程。使用原始SQL,每个SQL语句(至少)具有一次往返。