【问题标题】:Query execution takes too much time查询执行需要太多时间
【发布时间】:2013-06-02 18:56:01
【问题描述】:

我有以下问题

SELECT dbo.tblRegion.RegionName,
       dbo.tblDistributionLocation.DistributionLocationName,
       dbo.tblTSA.TSAName,
       TEmailInfo.EmailCM,
       COUNT(*) AS EmailCount
FROM   dbo.tblArea
       INNER JOIN dbo.tblTerritory
         ON dbo.tblArea.AreaID = dbo.tblTerritory.AreaID
       INNER JOIN dbo.tblDistribution
         ON dbo.tblTerritory.TerritoryID = dbo.tblDistribution.TerritoryID
       INNER JOIN dbo.tblDistributionLocation
         ON dbo.tblDistribution.DistributionID = dbo.tblDistributionLocation.DistributionID
       INNER JOIN dbo.tblRegion
         ON dbo.tblArea.RegionID = dbo.tblRegion.RegionID
       INNER JOIN dbo.tblTSA
         ON dbo.tblDistributionLocation.DistributionLocationID = 
                                                       dbo.tblTSA.DistributionLocationID
       INNER JOIN dbo.tblTSAEmail
         ON dbo.tblTSA.TSAID = dbo.tblTSAEmail.TSAID
       INNER JOIN (SELECT *
                   FROM   dbo.tblCMEvalEmail
                   WHERE  ( dbo.tblCMEvalEmail.EmailSentDate 
                    BETWEEN '2013-05-19 00:00:00' AND '2013-06-16 23:59:59' )) AS TCMEvalEmail
         ON dbo.tblTSAEmail.TSAEmail = TCMEvalEmail.EmailSenderEmail
       INNER JOIN (SELECT *
                   FROM   dbo.tblCMEvalEmailInfo
                   WHERE  dbo.tblCMEvalEmailInfo.EmailCMFacingDate 
                    BETWEEN '2013-05-19 00:00:00' AND '2013-06-16 23:59:59') AS TEmailInfo
         ON TCMEvalEmail.EmailID = TEmailInfo.EmailID
WHERE  ( dbo.tblTSA.TSAActive = 1 )
       AND TCMEvalEmail.EmailStatus = 'Success'
GROUP  BY dbo.tblRegion.RegionName,
          dbo.tblDistributionLocation.DistributionLocationName,
          dbo.tblTSA.TSAName,
          TEmailInfo.EmailCM 

这个查询有什么问题需要花费这么多时间?

但如果我缩短时间 '2013-05-20 00:00:00' 和 '2013-06-16 23:59:59' 那么它会回复得很快。我的查询有什么问题需要这么长时间?

【问题讨论】:

    标签: sql sql-server-2008


    【解决方案1】:

    性能调优不仅仅是按下一个神奇的开关 - 这是一项艰苦的工作。

    所以从最明显的开始:尝试将您的查询减少到绝对最低限度。

    例如

    • 为什么在您的内部查询中选择SELECT *,而您只使用该数据中的一个(或两个)列?只选择您真正需要的!

    在第一种情况下,如果我没记错的话,您只需要 EmailSenderEMail 列 - 所以只选择它!

    INNER JOIN 
    (
       select EmailSenderEmail 
       from dbo.tblCMEvalEmail 
       where (dbo.tblCMEvalEmail.EmailSentDate BETWEEN '2013-05-19 00:00:00' 
                                                   AND '2013-06-16 23:59:59') 
    ) as TCMEvalEmail  ON dbo.tblTSAEmail.TSAEmail = TCMEvalEmail.EmailSenderEmail 
    

    在第二种情况下,您需要将 EmailID 用于 JOIN,并在 SELECT 的输出中使用 EmailCM - 所以只选择这两列!

    INNER JOIN 
    (
        select EMailID, EMailCM
        from dbo.tblCMEvalEmailInfo 
        where dbo.tblCMEvalEmailInfo.EmailCMFacingDate BETWEEN '2013-05-19 00:00:00' 
                                                           and '2013-06-16 23:59:59'
     ) as TEmailInfo ON TCMEvalEmail.EmailID = TEmailInfo.EmailID 
    
    • 下一步:确保有适当的索引。如果您有这样的子选择,那么拥有一个覆盖您的查询的索引是非常有价值的,例如这将完全返回您需要的那些列。那么您在dbo.tblCMEvalEmail 上是否有带有EmailSenderEMail 列的索引?您在 dbo.tblCMEvalEmailInfo 上是否有包含两列 EMailID, EMailCM 的索引?

    • 另一件事:所有外键列都应该被索引,以提高 JOIN 操作的速度,并帮助加快外键约束检查。你这里使用的外键都被索引了吗?

    【讨论】:

    • 优秀的 cmets,但是用需要的字段替换 '*' 真的会提高性能吗?无论如何,SQL-Server 不这样做吗(即优化子选择中的表读取?)
    • SELECT * 在子查询中没有问题。 SQL Server 不会检索所有列,然后稍后将其丢弃,而只是内联子查询定义。例如查看 SELECT type FROM (SELECT * FROM master..spt_values) T 的执行计划,只需从 NCI 中检索 type 列。它不进行 CI 扫描。
    • @marc_s 替换子选择中的* 完全没有影响。虽然您所说的对于外部查询是正确的,但它对子选择或内部查询没有相同的影响,因为查询优化器实际上会将它们更改为仅在外部查询中提到的字段,因此它是自我优化的.
    【解决方案2】:

    可能没有合适的索引来进行连接。您可以优化两个子选择,但我怀疑查询优化器已经在这样做了。

    SELECT dbo.tblRegion.RegionName, 
            dbo.tblDistributionLocation.DistributionLocationName, 
            dbo.tblTSA.TSAName,
            TEmailInfo.EmailCM,
            COUNT(*) as EmailCount
    FROM dbo.tblArea 
    INNER JOIN dbo.tblTerritory
     ON dbo.tblArea.AreaID = dbo.tblTerritory.AreaID
    INNER JOIN dbo.tblDistribution
     ON dbo.tblTerritory.TerritoryID = dbo.tblDistribution.TerritoryID
    INNER JOIN dbo.tblDistributionLocation
     ON dbo.tblDistribution.DistributionID = dbo.tblDistributionLocation.DistributionID 
    INNER JOIN dbo.tblRegion
     ON dbo.tblArea.RegionID = dbo.tblRegion.RegionID 
    INNER JOIN dbo.tblTSA
     ON dbo.tblDistributionLocation.DistributionLocationID = dbo.tblTSA.DistributionLocationID 
    INNER JOIN dbo.tblTSAEmail
     ON dbo.tblTSA.TSAID = dbo.tblTSAEmail.TSAID 
    INNER JOIN dbo.tblCMEvalEmail
     ON dbo.tblTSAEmail.TSAEmail = tblCMEvalEmail.EmailSenderEmail
       AND dbo.tblCMEvalEmail.EmailSentDate BETWEEN '2013-05-19 00:00:00' 
                                                   AND '2013-06-16 23:59:59'
       AND tblCMEvalEmail.EmailStatus='Success'
    INNER JOIN dbo.tblCMEvalEmailInfo
     ON tblCMEvalEmail.EmailID = tblCMEvalEmailInfo.EmailID
       AND dbo.tblCMEvalEmailInfo.EmailCMFacingDate BETWEEN '2013-05-19 00:00:00' 
                                                           and '2013-06-16 23:59:59'
    WHERE (dbo.tblTSA.TSAActive = 1) 
    GROUP BY dbo.tblRegion.RegionName, 
              dbo.tblDistributionLocation.DistributionLocationName, 
              dbo.tblTSA.TSAName, TEmailInfo.EmailCM
    

    【讨论】:

    • 也有可能您会返回多条记录,而您不希望每封电子邮件返回超过 1 条记录,如果这种情况发生在超过 1 个表中,结果将呈指数增长.例如,如果 EmailID 在 tblCMEvalEmail 表中不是唯一的,或者 EmailSenderEmail 在 tblCMEvalEmail 表中不是唯一的(也许这是要加入的错误列?)
    【解决方案3】:

    正如 marc_s 所指出的,优化不是一件快速的事情,也不是一个一劳永逸的解决方案。最好的办法是阅读该主题(请参阅 http://beginner-sql-tutorial.com/sql-query-tuning.htm 了解一些入门技巧)。

    您还应该阅读 EXPLAIN PLAN 工具(或您的数据库的等效变体),这是一个重要的优化工具;它将突出显示可能会减慢您对特定数据库的查询速度的事情,例如全表扫描 - 消除这些通常会给您带来快速的胜利,并且通常会带来显着的改进。

    尽管如此,我突然想到的两件事是:

    1. 您是否在所有用于加入的 ID 上设置了索引?否则,这将对性能产生负面影响
    2. TCMEvalEmail.EmailStatus='Success' 是一个字符串匹配,通常比较慢;没有看到解释计划的结果很难说,但您可能需要考虑用数字状态代码替换它(例如,状态表的外键) - 但由于这可能是一项艰巨的任务,您应该只做如果解释计划将其作为一个问题突出显示。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-22
      相关资源
      最近更新 更多