【问题标题】:Why is my SQL query getting disproportionally slow when adding a simple string comparison?添加简单字符串比较时,为什么我的 SQL 查询变得异常缓慢?
【发布时间】:2017-06-28 13:43:22
【问题描述】:

所以,我有一个 MSSQL 的 SQL 查询,看起来像这样(为便于阅读而简化):

SELECT ...
FROM (
    SELECT ..., ROUND(SUM(TOTAL_TIME)/86400.0,2) ... 
    FROM MY_DATA
    WHERE STATUS NOT IN (107)
    GROUP BY ...
) q
WHERE q.Tdays > 0
GROUP BY ...   

它工作正常,但我需要与内部查询中的另一个表进行比较,所以我添加了一个左连接并表示比较:

SELECT ...
FROM (
    SELECT ..., ROUND(SUM(TOTAL_TIME)/86400.0,2) ... 
    FROM MY_DATA
    LEFT JOIN OTHER_TABLE ON MY_DATA.ID=OTHER_TABLE.ID //new JOIN
    WHERE STATUS NOT IN (107) AND (DEPARTMENT_ID='SP' OR DEPARTMENT_ID='BL') //new AND branch
    GROUP BY ...
) q
WHERE q.Tdays > 0
GROUP BY ...  

此查询有效,但比前一个查询慢很多。奇怪的是,注释掉 WHERE 子句的新 AND 分支,同时保留 JOIN 原样使其再次更快。好像它没有加入另一个正在减慢查询速度的表,但是实际的字符串比较......我不知道为什么这么慢,或者我如何加快速度......任何建议都将不胜感激!

【问题讨论】:

  • SQL 在比较字符串方面是出了名的慢——尤其是在它们没有被索引的情况下。不幸的是,我认为没有办法解决这个问题。在您的示例中,也许您可​​以创建一个带有数值(1 - 'SP'...2 - 'BL'...等)的部门表,加入该表,并根据数字进行比较。这可能会快很多,这就是为什么我总是在 PK 中使用整数字段,即使文本是唯一的(比如您的部门 ID)。
  • 在 DEPARTMENT_ID 上创建索引会对您有所帮助。
  • IDOTHER_TABLE 的主键吗?
  • 是的,两者都有。
  • 您应该查看/显示解释计划。 stackoverflow.com/questions/7359702/…

标签: sql sql-server


【解决方案1】:

使用INNER JOIN。无论如何,WHERE 子句正在撤消外连接:

SELECT ..., ROUND(SUM(TOTAL_TIME)/86400.0,2) ... 
FROM MY_DATA d INNER JOIN
     OTHER_TABLE ot
     ON d.ID = ot.ID //new JOIN
WHERE od.STATUS NOT IN (107) AND DEPARTMENT_ID IN ('SP', 'BL') //new AND branch
GROUP BY ...

IN 不应该有所作为;它只是更容易编写。)

接下来,如果这仍然有缓慢的性能,然后查看执行计划。这意味着 SQL Server 做出了错误的决定,可能是在 JOIN 算法上。通常,我通过禁止嵌套循环连接来解决此问题,但也可能有其他解决方案。

【讨论】:

  • 您的代码示例仍然包含左连接 - 我想这只是一个疏忽/错字。用 INNER JOIN 替换 LEFT JOIN 并没有明显加快速度......我必须研究执行计划 - 感谢您提供信息。
【解决方案2】:

如果没有看到执行计划,很难明确地说什么会或不会加快速度。此外,了解您需要多快会影响您可能想要(或不想)考虑采取的步骤。

诚然,接下来的内容有些模糊,但当我想到这一点时,这些是我想到的一些事情。看看 Philip Couling 在那个很好的链接中建议的执行计划,以了解痛点在哪里,当然,对这些建议持保留态度。

  • 您可以考虑向其中一个或两个表中添加一些索引。执行计划甚至可能会为您提供有用的建议,但在我看来,OTHER_TABLE.DEPARTMENT_ID 上的某些内容可能不会有什么坏处。
  • 如果您知道那些硬编码的搜索词(例如 STATUSDEPARTMENT_ID 总是相同的),您也许可以将潜在的新索引构建为 Filtered Indexes
  • 您可以预先计算其中的一些信息,前提是这些信息的变化速度不是很快,以至于您需要在每次调用时都重新查询它。这又回到了您需要多快的速度,因为对于几乎任何查询,您都可以添加列或预先填充的查找表以避免在运行时进行工作。例如,您可以创建一个新的位字段,如 IsNewOrBranchIsStatusNot107(这两个步骤都有些令人震惊,但可以工作)。或者这可能是提前在内部查询中预先聚合数据。
  • 我知道您为了我们的利益简化了查询,但这也使我们很难知道子查询发生了什么,以及随后针对该子查询执行的GROUP BY。可能有一种方法可以避免进行两次分组。
  • 同样,如果 SQL 难以确定如何最好地返回数据,您也可以考虑将正在执行的操作拆分为单独的语句。例如,您可以使用内部查询的结果填充临时表或表变量,然后对其执行后续GROUP BY。虽然这种方法并不总是有用,但很多时候尝试将所有工作都塞进一个查询实际上最终会比几个单独的、简单的、优化的步骤更糟糕。
  • 正如 Gordon Linoff 所建议的,有许多查询提示用于诱使执行计划以特定方式执行操作。但要小心,这往往是一种疯狂。

【讨论】:

    【解决方案3】:

    您的 SQL 很好,使用附加的 AND 子句限制您的数据通常不会使其变慢。

    事实上,选择快速执行路径是一个难题,SQL Server 有时(尽管很少)会出错。

    帮助 SQL Server 找到最佳执行路径的方法是:

    • 确保statistics on your tables are up-to-date
    • 确保有一个 SQL Server 可以使用的“明显合适”的索引。 SQL Server Management Studio 通常会在选择“显示实际执行计划”选项时为您提供有关缺失索引的建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-21
      • 1970-01-01
      • 1970-01-01
      • 2014-11-04
      • 2019-11-09
      • 1970-01-01
      • 2021-07-27
      相关资源
      最近更新 更多