【问题标题】:Is a dynamic sql stored procedure a bad thing for lots of records?对于大量记录来说,动态 sql 存储过程是一件坏事吗?
【发布时间】:2010-09-23 23:33:25
【问题描述】:

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

【问题讨论】:

  • 当您提出最终解决方案时,请告诉我们,我将更新我的答案,以确保我们讨论的有效技术/故障排除得到明确。

标签: sql sql-server tsql stored-procedures


【解决方案1】:

与动态 SQL 相比,您的索引(或缺少索引)更有可能导致速度变慢。

执行计划是什么样的?在 SSMS 中执行相同的查询是否很慢?当它在存储过程中时呢?

如果您的表是未索引堆,则随着记录数量的增长,它的性能会很差——这与查询无关,而动态查询实际上可以随着表性质的变化而更好地执行,因为动态查询更有可能当它不在缓存中时重新评估它的查询计划。这通常不是问题(我不会将其归类为动态查询的设计优势),除非在系统的早期阶段 SP 尚未重新编译,但统计信息和查询计划已过时,但数据发生了翻天覆地的变化。

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

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

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

【讨论】:

  • 存储过程中的动态查询实际上似乎比存储过程中的非动态查询运行得更快。两者的执行计划看起来是一样的。
  • 它是否使用任何索引?您确定缓慢的标准是什么 - 与什么比较?
  • 是的,任何我加入的列都有索引。我只是在 ssms 中运行静态 proc 和动态 proc 并检查返回所有结果所需的时间(动态 23 秒,静态 45 秒)
  • 如果没有更多关于表模式的详细信息(以及返回的记录数 - 请记住,如果您的查询将它们拉下来,它们可能都需要走线)和索引,很难说.您是否通过索引调优分析器运行查询?
  • 还不是静态的。我有动态查询,但它没有提供任何优化。如果我用静态查询运行它并给出建议,应用它们会影响动态查询吗?
【解决方案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)

【讨论】:

  • 所有参数都是可选的。这对优化有何帮助?
【解决方案3】:

“动态”和“静态”SQL 的唯一区别在于解析/优化阶段。完成这些操作后,查询将同样运行。

对于简单的查询,这个解析阶段加上网络流量占总事务时间的很大一部分,因此最好尝试减少这些时间。

但是对于大而复杂的查询,与优化器选择的实际路径相比,这种处理总体上是微不足道的。

我会专注于优化查询本身,如果您认为合适的话,可能包括非规范化,尽管我不会在第一次尝试时这样做。

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

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        如前所述,如果您正在执行大规模查询,索引是首先要查看的瓶颈。确保对大量查询的列进行索引。此外,请确保您的查询在检查未索引参数之前检查所有索引参数。这可以确保首先使用索引过滤结果,然后仅在必要时才进行慢速线性搜索。因此,如果 col2 被索引但 col1 没有被索引,它应该如下所示:

        WHERE col2 = @col2 AND col1 = @col1
        

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

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

        我喜欢上面的Dave Kemp's answer

        【讨论】:

          【解决方案7】:

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

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

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

          【讨论】:

            【解决方案8】:

            不是动态 Sql 的粉丝,但如果你坚持使用它,你可能应该阅读这篇文章: http://www.sommarskog.se/dynamic_sql.html 他真的深入探讨了使用动态 SQL 的最佳方法以及使用它可能产生的问题。

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

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

            【讨论】:

              【解决方案9】:

              我已经通过以下逻辑取得了一些成功(在有限数量的情况下):

              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
              

              【讨论】:

              • 你在回答什么问题?
              • 我用它来消除动态 sql 并利用 2 个语句级查询计划。如前所述,执行“AND (@EarliestDate is Null OR PublishedDate
              【解决方案10】:

              如果您试图优化到低于 1s 的范围,那么估计解析和编译动态 sql 相对于实际查询执行时间所需的大致时间可能很重要:

               SET STATISTICS TIME ON;
              

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

               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.
              

              索引优化无疑会大大移动 199 毫秒的障碍(除非可能是由于编译时包含的一些分析/优化)。

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

              【讨论】:

                猜你喜欢
                • 2019-11-04
                • 1970-01-01
                • 2010-10-19
                • 2014-11-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-10-07
                相关资源
                最近更新 更多