什么时候写“ad hoc sql”vs存储过程比较好?
我通过我的应用程序有100%的特设SQL。 我的一个好友build议我将存储过程转换为额外的性能和安全性。 这在我的脑海里提出了一个问题,除了速度和安全性还有其他的理由坚持特设的SQL查询?
SQL Servercaching临时查询的执行计划,所以(折扣第一次调用所花费的时间)这两种方法在速度上是一致的。
一般来说,存储过程的使用意味着将应用程序所需的一部分代码(T-SQL查询)并放在一个不受源代码控制的地方(它可以是,但通常不是 );在不知情的情况下可以被他人改变的地方。
在这样的中心位置查询可能是一件好事,取决于有多less不同的应用程序需要访问它们所代表的数据。 我通常会发现,保留驻留在应用程序代码本身的应用程序使用的查询要容易得多。
在二十世纪九十年代中期,传统的观点认为SQL Server中的存储过程是在性能危急的情况下进行的,当然也是如此。 然而,这个CW背后的原因还不是很长时间。
更新:另外,经常在关于存储过程的可行性的辩论中,需要防止SQL注入被调用来防御特效。 当然,没有一个人正确地认为,通过string连接来组装即席查询是正确的事情(尽pipe如果你连接用户input,这只会使你受到SQL注入攻击)。 显然特别的查询应该是参数化的,不仅可以防止sql注入攻击的怪物,而且也可以让你的程序员更容易一些(除非你喜欢弄清楚何时使用单引用你的价值)。
更新2:我做了更多的研究。 基于这个MSDN白皮书 ,看起来答案取决于你的意思是“特别”与您的查询,确切地说。 例如,像这样的一个简单的查询:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5
… 将执行计划caching。 此外,因为查询不包含某些不合格的元素(比如几乎任何一个表中的简单SELECT),SQL Server将实际上“自动参数化”查询并用参数replace文字常量“5”,并caching参数化版本的执行计划。 这意味着如果你执行这个即席查询:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23
…它将能够使用caching的执行计划。
不幸的是,用于自动参数化的不合格查询元素的列表很长(例如,忘记使用DISTINCT
, TOP
, UNION
, GROUP BY
OR
等),所以你真的不能指望性能。
如果你有一个“超级复杂”的查询,不会自动参数化,如:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23
…它仍然会被查询的确切文本caching,所以如果你的应用程序重复使用相同的文字“硬编码”值来调用这个查询,那么每个查询之后的每个查询都将重新使用caching的执行计划(和因此与存储的proc一样快)。
如果文字值发生变化(例如基于用户操作,例如筛选或sorting查看的数据),那么查询将不会从caching中受益(偶尔当偶然地匹配最近的查询时)。
从“临时”查询caching中受益的方法是对它们进行参数化。 在C#中像这样创build一个查询:
int itemCount = 5; string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + itemCount.ToString();
是不正确的。 正确的方法(使用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(); }
该查询不包含文字,并且已经完全参数化,因此使用相同参数化语句的后续查询将使用caching计划(即使用不同的参数值调用)。 请注意,这里的代码实际上和用于调用存储过程的代码几乎相同(唯一的区别是CommandType和CommandText),所以它有点归结到你希望查询文本“存在“(在您的应用程序代码或存储过程中)。
最后,如果通过“ad-hoc”查询,则意味着您正在dynamic构build具有不同列,表格,过滤参数以及其他内容的查询,如下所示:
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
…那么你几乎不能用存储过程来做到这一点(没有EXEC
黑客在礼貌的社会中是不会被提及的),所以问题是哑巴。
更新3:这是唯一一个非常好的性能相关的原因(我可以想到,无论如何)使用存储过程。 如果你的查询是一个长期运行的程序,编译执行计划的过程比实际的执行时间要长得多,并且查询只是很less被调用(比如像月度报告),那么把它放到存储过程中可能会使SQL Server将编译后的计划保存在caching中的时间足够长,以便它在下个月左右。 但是,如果这是真的,那就打我吧。
没有什么关于存储过程,使他们神奇更快或更安全。 有些情况下,对于某些types的任务,devise良好的存储过程可能会更快,但对于临时SQL也是如此。
编码你find最有成效的方式。
“在你做得更快之前做好准备。” – Brian Kernighan
如果您没有编写存储过程,请调查参数化查询 。 如果您自己构buildSQL(包括参数串联),则会引发SQL注入攻击 。
有几个关于这个话题的神话,你应该消除:
误解1:存储过程是预编译的
http://scarydba.wordpress.com/2009/09/30/pre-compiled-stored-procedures-fact-or-myth/
神话2:特设SQL查询不重用执行计划: http : //scarydba.wordpress.com/2009/10/05/ad-hoc-queries-dont-reuse-execution-plans-myth-or-fact/
恕我直言,procs有优势,当你绝对需要locking数据库。 在这些情况下,您可以使用只有权执行存储过程的帐户。 另外,他们可以从DBA的angular度为应用程序和数据库之间提供一个抽象层。
同样,dynamicSQL在查询可能需要更改某些并且是…好…dynamic的情况下更好。 或者如果你知道你必须移植到多个数据库。
只要所有用户input的值都被参数化,两者就像SQL注入一样安全。
关于这个线程的性能,caching和安全性已经有很多的说法了,我不会重复这些观点。 在这个线程中还有一些我还没有读的东西,那就是可移植性问题和rountrips。
- 如果您对跨应用程序编程语言的应用程序的最大可移植性感兴趣,那么存储过程是一个好主意:您在应用程序外存储在数据库中的程序逻辑越多,如果要移动到另一个框架,则必须重新编码的次数越less;语言。 另外,调用存储过程的代码比实际的原始SQL本身要小得多,所以应用程序代码中的数据库接口将占用较小的空间。
- 如果在多个应用程序中需要相同的逻辑,那么存储过程可以很方便地具有该逻辑的单个定义,并可以被其他应用程序重新使用。 然而,这种好处并没有被夸大,因为你也可以在你跨应用程序共享的库中分离出这个逻辑。 当然,如果应用程序使用不同的语言,那么在存储过程中就会有真正的好处,因为通过语言的db接口调用过程可能比链接到用其他语言编写的库更容易。
- 如果您对RDBMS可移植性感兴趣,那么存储过程很可能成为您最大的问题之一。 所有主要和次要RDBMS-es的核心特征都非常相似。 可以在存储过程的语法和可用的内置function中find最大的差异。
关于往返:
- 如果您的应用程序中有多个多语句事务或一般来说需要多个SQL语句的函数,那么如果您将这些多语句放入存储过程中,性能可能会提高。 原因是调用存储过程(可能返回多个结果)只是一个单一的rountrip。 对于原始SQL,每个SQL语句至less有一次往返。
我通过我的应用程序有100%的特设SQL。 我的一个好友build议我将存储过程转换为额外的性能和安全性。
除非有实际的痛点,否则我不会担心performance。 例如,有人正在使用您的应用程序,并抱怨说速度很慢。 直到你达到那个点,你的时间是最好的改善你的应用程序的花费。
在安全方面,你必须平衡努力与风险。 如果您的网站没有存储任何有价值的东西,甚至SQL注入也是一个完全可以接受的风险,正如许多网站所certificate的:)
可移植性,当你需要切换到一个新的SQL服务器,并没有访问旧的。
我曾经参与过一些项目,原来的开发人员在被解雇后不会访问服务器,所以我们不得不自己重写许多查询,因为他们被锁在了我们没有的存储过程中许可查看。 如果所有的查询都在源代码中,那将使得它更容易。
Ad-Hoc查询何时可以带来任何好处,我无法看到。 与朋友讨论同样的问题,我们发现下面的事情支持存储过程(除了明显的caching/ SQL注入问题):
1)代码可读性:如果你的应用程序中embedded了一些毛茸茸的SQL代码,那么阅读/理解就变得困难了。 有一个很好的命名约定存储过程,它确切地说明了它是什么,而不是很多的代码。 我们试图在重构时使用的原则更less。
2)在不重新部署的情况下改进应用程序的能力:如果由于缺陷或性能不佳而需要调整逻辑,将SQL代码embedded到应用程序中意味着需要重新部署(即使数据集没有更改) 。 如果你有一个存储过程,你需要重新部署的唯一事情就是这个过程。 另外,它给DBA带来了变化,通过使用该过程来提高查询的整体性能。 如果您使用embedded式版本,这将会变得更加困难。
3)networkingstream量:如果你传递了大量的SQL代码,就会增加在networking上传输的消息(或者RPC调用)的大小,由于请求可能导致性能下降。 特别是如果有许多用户每次都调用相同的调用。
我希望这有帮助。
干杯,瓦格纳。
我的答案可能有点偏离主题,但无论如何:
在处理视图时可能很有用。 例如,hibernate(你没有使用),对视图的支持很不好。 当我需要使用它查询视图时,我总是使用原始SQL,因为这是唯一可行的。
根据我的经验,我会为每种方法提供一个优点。
Ad-Hoc SQL的要点:
有一种情况是,Ad-Hoc SQL不太痛苦(最初),这涉及到大量的(和dynamic的)search参数范围。 比方说,你有一个账户search和一个伴随的高级账户search,其中包含3倍多的参数。 一些领域很容易(帐号); 其他人可能会非常痛苦(在地址线1上的子stringsearch!)而最糟糕的是,大多数参数不是必需的。
这使得性能调整不是微不足道的。 在这种情况下,具有如此多的参数组合,caching执行计划(我的经验是MS SQL)将适得其反 。 例如只提供Account#和Salesman#的执行计划对另一组search,提供地址search和他们携带的品牌可能是非常不利的。 Ad-Hoc SQL将根据提供的不同参数集合具有重新编译的副作用,从而避免执行计划caching问题。
当然,上面的操作也可以通过存储过程完成,但是为了解决执行计划caching问题,您必须在存储过程中启动重新编译命令。 人们也可以争辩说,dynamic的SQL结构可以放在存储过程中,但这只是将Ad-Hoc SQL移到另一个地方; 还是Ad-Hoc SQL。
存储过程点:
可以将存储过程视为API。 如果你曾经在企业环境中工作过,那么很可能会有不同的应用程序在做同样的事情。 例如,事件表可以从不同的软件插入,包括会计,销售,审计,客户关系pipe理等。这些程序可能不是由同一组人员(例如不同的分部)维护的,但是他们最终将访问到相同的底层数据库。
在这种情况下,使用Ad-Hoc SQL将成为源代码pipe理的噩梦,因为这将导致Ad-Hoc SQL的多个版本执行相同的function,其中每个版本可能具有不同的副作用。 我目前正在处理这种情况,这并不好玩。 这种情况下的存储过程可以被重用,因此具有对数据库代码的集中pipe理。
事实上,SQL注入可以通过参数化您的查询来防止(例如查看ODBCParameters),并且您的查询可以被构build为使得这些参数不能被SQL注入。 例如…
DECLARE @param varchar(50) SELECT @param = ? SELECT * FROM TABLE WHERE NAME = @param
是使用ODBC参数进行内部查询的安全方法。 但是,使用存储过程有一些优点:
- 具有多个函数或游标的复杂SQL如果以特别的方式使用,可能会搞砸,因为某些ODBC驱动程序不知道如何处理多个查询请求
- 它将业务逻辑从数据库调用中分离出来。 这允许您(应用程序开发人员)对数据库的结构一无所知,并且仍然开发您的应用程序,而专用DBA或SQL开发人员可以微调SQL。
- 存储过程是预先编译的,所以在很多次重复时,它们会更快,但是只有当它们被非常频繁地调用时(即监视程序)
当所有的事情都说完之后,这是一个devise决定。 不要考虑到可移植性,你可以让SQL开发人员给你脚本来应用存储过程,并在程序的安装上运行它们:)
希望这可以帮助
临时查询为您提供了灵活的应用程序逻辑,但您几乎总是要为性能付出代价。
如果你关心性能,我可能会同意你的朋友,你应该看看存储过程,或更多的静态forms的查询,以允许数据库“预先优化”的查询,或允许caching层如果存在的话)潜在地caching查询结果。
如果您每次都在飞行中生成查询,则数据库很可能无法帮助您完成性能。
- 您不需要编写存储过程
- 一般来说,临时SQL足够快。 您可以使用参数化查询来提高速度。
- 您可以将基于string的SQL编译为“已编译”的SQL,然后执行,速度要快得多。
- 通常,SQL的性能瓶颈是上面没有的查询。
这可能取决于谁还在使用数据库。 如果只有一个应用程序使用db,那么参数化查询的优点是坐在源代码中。
如果其他应用程序使用db,那么dba应该将公用存储过程放在db中。
存储过程的一个优点是数据不必通过networking传输以进行中间计算。 如果大量计算结果为单个值,那么存储过程将会更快。
没有正确的答案。 这取决于每种情况。
有没有人提到这个? 存储过程通常会返回每个字段,为每个您想要的字段的变体创build一个字段是不可行的。 Ad-hoc可以让你只指定你想要的。 但是,如果您正在使用任何types的实体(自定义对象,EF等),您可能会返回所有字段。
有可能不会有性能上的好处,但为了可维护性,您可能需要考虑使用像LINQ2SQL这样的东西,以便在SQL中没有语法错误。