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

时间:2010-04-29 00:18:29

标签: c# sql sql-server-2008

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

17 个答案:

答案 0 :(得分:66)

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

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

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

在20世纪90年代中期,传统观点认为SQL Server中的存储过程是处于性能关键情况的方式,当时它们肯定是。然而,这个CW背后的原因在很长一段时间内都没有效。

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

更新2:我做了更多研究。基于this MSDN white paper,答案似乎取决于您对查询的“ad-hoc”的含义。例如,一个简单的查询:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

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

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

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

不幸的是,自动参数化的不合格查询元素列表很长(例如,忘记使用DISTINCTTOPUNIONGROUP BY,{{ 1}}等等,所以你真的不能指望它的性能。

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

OR

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

如果文字值发生变化(例如,基于用户操作,如过滤或排序已查看的数据),那么查询将无法从缓存中受益(有时候,他们会偶然意外地匹配最近的查询)。

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

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

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

int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

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

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

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();
}

...那么你几乎不能使用存储过程(没有礼貌社会中没有提到的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 hack)这样做,所以重点是没有实际意义。

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

答案 1 :(得分:18)

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

以最有效率的方式编码。

  

“在你加快速度之前做好准备。” - Brian Kernighan

答案 2 :(得分:12)

如果您不编写存储过程,请调查parameterized queries。如果您自己构建SQL(包括参数连接),则需要SQL injection attack

答案 3 :(得分:8)

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

误区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注入方面都是安全的。

答案 4 :(得分:5)

在这个帖子中已经有很多关于性能,缓存和安全性的说法,我不会重复这些观点。在这个帖子中我还有一些我还没有读过的东西,那就是可移植性问题和rountrips。

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

关于往返行程:

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

答案 5 :(得分:2)

可移植性,当您需要切换到新的SQL服务器,并且无法访问旧的SQL服务器时。

我曾经做过一些项目,原始开发人员在被解雇后不会访问服务器,因此我们不得不重新编写许多查询,因为他们被锁定在我们没有的存储过程中没有权限查看。如果所有查询都在源代码中,那么它将使它变得更容易。

答案 6 :(得分:2)

  

我有100%的临时sql   应用。我的朋友   建议我转换为存储   额外表现的程序   和安全。

在有实际的痛点之前,我不担心表现。例如,有人正在使用您的应用程序,并抱怨它很慢。在达到这一点之前,您最好花时间改进应用程序。

在安全方面,您必须平衡努力与风险。如果您的网站没有存储任何有价值的内容,即使SQL注入也是一个完全可以接受的风险,正如许多网站所证明的那样:)

答案 7 :(得分:2)

我无法看到Ad-Hoc查询何时会带来任何好处。与朋友讨论同样的问题,我们发现以下事情支持存储过程(除了明显的缓存/ SQL注入问题):

1)代码可读性:如果您的应用程序中嵌入了一些毛茸茸的SQL代码,那么阅读/理解就会变得更加困难。对存储过程有一个好的命名约定要简单地说明它的作用,而不是很多代码。这与我们在重构时尝试使用的原则相同。

2)能够在不重新部署的情况下改进应用程序:如果由于错误或性能不佳需要调整逻辑,在应用程序中嵌入SQL代码意味着您需要重新部署它(即使您的数据集没有'改变)。如果您在存储过程中拥有它,则唯一需要重新部署的是该过程。此外,它通过使用该过程为DBA提供了更改,从而提高了查询的整体性能。如果你正在使用嵌入式版本,这将更加困难。

3)网络流量:如果您传递大量SQL代码,则会增加通过网络传输的消息(或RPC调用)的大小,这可能会因请求而导致性能下降。特别是如果每​​次都有多个用户同时打电话。

我希望这会有所帮助。

干杯,瓦格纳。

答案 8 :(得分:1)

我的回答可能稍微偏离主题,但无论如何:

使用视图时可能很有用。例如,Hibernate(你没有使用)对视图的支持非常糟糕。当我需要用它查询视图时,我总是使用原始SQL,因为这是唯一可行的。

答案 9 :(得分:1)

说实话,可以通过参数化查询来防止SQL注入(例如查看ODBCParameters),并且可以构造查询,使得这些参数不能被SQL注入。例如......

DECLARE @param varchar(50)
SELECT @param = ?
SELECT * FROM TABLE WHERE NAME = @param

是使用ODBC参数执行内部查询的安全方法。但是,使用存储过程有一些优点:

  1. 如果以临时方式使用,具有多个函数或游标的复杂SQL可能会搞砸,因为某些ODBC驱动程序不知道如何处理多个查询请求
  2. 它将业务逻辑与数据库调用分开。这允许您(应用程序开发人员)对数据库的结构一无所知,并且仍然可以开发您的应用程序,而专用的DBA或SQL开发人员可以对SQL进行微调。
  3. 存储过程是预先编译的,所以经过多次重复,它们会更快,但只有非常频繁地被调用(即监控程序)
  4. 当一切都说完了,这是一个设计决定。不要考虑可移植性,您可以让SQL开发人员为您提供应用存储过程的脚本,并在安装程序时运行它们:)

    希望这有帮助

答案 10 :(得分:1)

存储过程的一个优点是不必通过网络传输数据以进行中间计算。如果大量计算产生单个值,那么存储过程会更快。

答案 11 :(得分:1)

根据我的经验,我会为每种方法提供一个有利的观点。

指向Ad-Hoc SQL:

有一种情况是Ad-Hoc SQL不那么痛苦(最初),这涉及大范围(和动态)的搜索参数范围。例如,假设您有一个帐户搜索和一个配套的高级帐户搜索,其中包含3倍以上的参数。有些领域很简单(账号#);其他可能非常痛苦(在地址行1上搜索子字符串!)最坏的情况是,大多数参数都不是必需的。

这使性能调整变得微不足道。在这种情况下,拥有如此多的参数组合,执行计划的缓存(我的经验是在MS SQL上)将逆火。例如,仅提供Account#和Salesman#的执行计划对于另一组搜索可能非常糟糕,提供地址搜索及其携带的品牌。 Ad-Hoc SQL将根据提供的不同参数集重新编译,从而解决执行计划缓存问题。

当然,上述操作也可以通过存储过程完成,但为了解决执行计划缓存问题,您必须在存储过程中启动重新编译命令。还可以说动态SQL构造可以放在存储过程中,但这只是将Ad-Hoc SQL移动到另一个地方;仍然是Ad-Hoc SQL。

存储过程的要点:

可以将存储过程视为API。如果您曾在企业环境中工作过,那么很可能会有不同的应用程序执行完全相同的操作。例如,事件表可以从不同的软件中插入,包括会计,销售,审计,客户关系管理等。这些程序可能不是由同一组人员(例如,不同的子部门)维护,但他们最终可以访问到同一个底层数据库。

在这种情况下,它将是使用Ad-Hoc SQL的源代码管理噩梦,因为这将导致多个版本的Ad-Hoc SQL执行相同的功能,其中每个版本可能具有不同的副作用。我目前正处理这种情况,这并不好玩。在这种情况下,存储过程可以重复使用,因此可以集中管理数据库代码。

答案 12 :(得分:0)

  1. 您不需要编写存储过程
  2. 一般来说,ad-hoc SQL足够快。您可以使用参数化查询来提高速度。
  3. 您可以将基于字符串的SQL编译为“已编译”的SQL,然后执行该操作,这要快得多。
  4. 通常,SQL的性能瓶颈是上述任何一种查询。

答案 13 :(得分:0)

即席查询为您提供应用程序逻辑的灵活性,但您几乎总是为性能付出代价。

如果你关心性能,我可能会同意你的朋友你应该查看存储过程或一些更静态的查询,以允许数据库“预优化”查询,或允许缓存层(如果存在)以潜在地缓存查询结果。

如果您每次都在运行时生成查询,那么数据库很可能根本无法帮助您提高性能。

答案 14 :(得分:0)

这可能取决于还有谁在使用数据库。如果只有一个应用程序使用数据库,则参数化查询的优势在于可以放在源代码中。

如果其他应用程序使用db,则dba应将公共存储过程放在db。

答案 15 :(得分:0)

没有“正确”的答案。这取决于每种情况。

有没有人提到这个?存储过程通常返回每个字段,并且为每个字段创建一个所需的字段是不可行的。 Ad-hoc允许您仅指定所需的内容。但是,如果您使用任何类型的实体(自定义对象,EF等),您可能会返回所有字段。

答案 16 :(得分:0)

可能没有性能优势,但是为了可维护性,您可能希望使用LINQ2SQL之类的东西,以便在SQL中没有语法错误。