动态sql存储过程对于大量记录来说是一件坏事吗?

时间:2008-11-25 15:33:17

标签: sql sql-server tsql stored-procedures

我有一个包含近800,000条记录的表,我目前正在使用动态sql在后端生成查询。前端是一个搜索页面,它占用大约20个参数,并且根据是否选择了参数,它会向基本查询添加“AND ...”。我很好奇动态sql是否是正确的方法(似乎不是因为它运行缓慢)。我正在考虑用我的所有数据创建一个非规范化表。这是一个好主意还是我应该一起构建查询,而不是使用动态sql逐个构建它。最后一点,有没有办法加速动态sql?

10 个答案:

答案 0 :(得分:10)

您的索引(或缺少索引)更有可能导致比动态SQL慢。

执行计划是什么样的?在SSMS中执行时查询速度是否相同?什么时候它在存储过程中?

如果你的表是一个未编制索引的堆,它会随着记录数量的增长而表现不佳 - 这与查询无关,并且动态查询实际上可以更好地执行,因为表格性质会发生变化,因为动态查询更有可能当它不在缓存中时,重新评估其查询计划。这通常不是问题(我不会将其归类为动态查询的设计优势),除非在系统的早期阶段没有重新编译SP,但统计和查询计划已经过时,但是数据刚刚发生了巨大的变化。

还不是静态的。我有动态查询,但它没有给出任何优化。如果我使用静态查询运行它并给出了建议,应用它们会影响动态查询吗? - Xaisoft(41分钟前)

是的,除非您分析了工作负载文件,否则可能不会分析动态查询(EXEC(@sql))。 - Cade Roux(33分钟前)

当您在多个连接的表中进行搜索查询时,带索引的列需要是搜索列以及主键/外键列 - 但它取决于各个表的基数。调谐分析仪应该显示这一点。 - Cade Roux(22分钟前)

答案 1 :(得分:5)

我只想指出,如果你使用这种可选参数:

AND (@EarliestDate is Null OR PublishedDate < @EarliestDate)

查询优化器在生成查询计划时不知道参数是否存在。我见过优化器在这些情况下做出错误选择的情况。更好的解决方案是构建仅使用所需参数的sql。在这些情况下,优化器将制定最有效的执行计划。请务必使用参数化查询,以便它们可以在计划缓存中重复使用。

答案 2 :(得分:4)

如上一个答案,请检查您的索引和计划。

问题是您是否使用存储过程。从你的措辞来看,这并不明显。存储过程在运行时创建查询计划,并保留该计划直到重新编译。使用不同的SQL,您可能会遇到错误的查询计划。你可以做几件事:

1)将WITH RECOMPILE添加到SP定义,这将导致每次执行都生成新计划。这包括一些开销,这是可以接受的。

2)根据提供的参数,使用单独的SP。这将允许更好的查询计划缓存

3)使用客户端生成的SQL。这将每次创建一个查询计划。如果使用参数化查询,则可能允许您使用缓存的查询计划。

答案 3 :(得分:3)

“动态”和“静态”SQL之间的唯一区别是解析/优化阶段。完成后,查询将以相同的方式运行。

对于简单查询,此解析阶段加上网络流量在总交易时间中占很大比例,因此尝试减少这些时间是一种好习惯。

但对于大型复杂查询,与优化程序选择的实际路径相比,此处理总体上无关紧要。

我会专注于优化查询本身,包括如果你认为它是合适的,可能是非规范化,但我不会在第一次自己做的时候这样做。

例如,有时可以在应用程序中使用缓存查找表在“运行时”完成非规范化,而不是将其维护在数据库中。

答案 4 :(得分:3)

不是动态Sql的粉丝,但如果你坚持使用它,你应该阅读这篇文章: http://www.sommarskog.se/dynamic_sql.html 他真正深入研究了使用动态SQL的最佳方法,使用它的isues可以创建。

正如其他人所说,索引是最可能的罪魁祸首。在索引中,人们经常忘记做的一件事就是在FK字段上放置一个索引。由于PK会自动创建索引,因此许多人也会假设FK。不幸的是,创建FK不会创建索引。因此,请确保您加入的任何字段都已编入索引。

可能有更好的方法来创建动态SQL,但没有看到代码,很难说。我至少会查看它是否正在使用子查询,而是用派生表连接替换它们。此外,任何使用游标的动态SQl都会很慢。

答案 5 :(得分:2)

如果参数是可选的,那么经常使用的技巧就是创建一个这样的过程:

CREATE PROCEDURE GetArticlesByAuthor (
    @AuthorId int,
    @EarliestDate datetime = Null )
AS
   SELECT  * --not in production code!
   FROM Articles
   WHERE AuthorId = @AuthorId
   AND (@EarliestDate is Null OR PublishedDate < @EarliestDate)

答案 6 :(得分:1)

这里有一些很好的带有可选搜索条件的查询示例:How do I create a stored procedure that will optionally search columns?

答案 7 :(得分:1)

如上所述,如果您正在进行大规模查询,则索引是第一个需要关注的瓶颈。确保对查询量很大的列进行索引。此外,请确保查询在检查未编入索引的参数之前检查所有索引参数。这样可以确保首先使用索引过滤结果,然后仅在必须时进行慢速线性搜索。因此,如果col2被索引但col1不是,则它应如下所示:

WHERE col2 = @col2 AND col1 = @col1

你也可能想要过度使用索引,但请记住,过多的索引会导致写入速度慢和磁盘使用量过大,所以不要太疯狂。

如果我可以出于两个原因,我会避免动态查询。一,他们不保存查询计划,因此每次都会编译语句。另一个是他们很难操纵,测试和排除故障。 (他们看起来很难看。)

我喜欢上面的Dave Kemp's answer

答案 8 :(得分:0)

我已经取得了一些成功(在有限数量的实例中),具有以下逻辑:

CREATE PROCEDURE GetArticlesByAuthor (    
    @AuthorId int,    
    @EarliestDate datetime = Null 
    ) AS   

SELECT SomeColumn
FROM Articles   
WHERE AuthorId = @AuthorId   
AND @EarliestDate is Null
UNION
SELECT SomeColumn
FROM Articles   
WHERE AuthorId = @AuthorId   
AND PublishedDate < @EarliestDate

答案 9 :(得分:0)

如果您尝试优化到低于1s范围,那么相对于实际查询执行时间来判断大约需要多长时间来解析和编译动态sql可能很重要:

 SET STATISTICS TIME ON;

然后“静态”执行动态SQL字符串并检查“消息”选项卡。我对这个~10行动态sql查询的结果感到惊讶,该查询返回1M行表中的两行:

 SQL Server parse and compile time: 
    CPU time = 199 ms, elapsed time = 199 ms.

 (2 row(s) affected)

 SQL Server Execution Times:
     CPU time = 0 ms,  elapsed time = 4 ms.

索引优化肯定会大大超过199ms的障碍(除非可能是由于编译时包含的一些分析/优化)。

但是,如果动态SQL使用参数或重复,则编译结果可能会根据:See Caching Query Plans缓存,这将消除编译时间。知道缓存条目的生存时间,大小,会话之间共享等等,会很有趣。