如何优化这些 T-SQL 查询?

Posted

技术标签:

【中文标题】如何优化这些 T-SQL 查询?【英文标题】:How can I optimize these T-SQL queries? 【发布时间】:2016-06-22 10:57:19 【问题描述】:

我有四个以完全相同的方式计算的 sql 变量,除了每个查询有一个条件:

  C.CenterID = @CenterID
  C.CenterNo IN (SELECT CenterNo from Registry.Vol1_5)
  C.CenterNo IN (SELECT CenterNo from Registry.Vol6_10)
  C.CenterNo IN (SELECT CenterNo from Registry.Vol11)

我的代码在这里:

DECLARE @MyCenterAllComplications INT;
DECLARE @Total5AllComplications INT;
DECLARE @Total6_10AllComplications INT;
DECLARE @Total11AllComplications INT;   


SET @MyCenterAllComplications = (SELECT count(*) FROM ECLS.Runs R

 INNER JOIN Registry.Patients P on R.PatientId = P.PatientId
 INNER JOIN Registry.CenterPatients CP ON CP.PatientId = P.PatientId
 INNER JOIN Registry.Centers C on C.CenterId = CP.CenterId

where 1 = 1

 AND NOT R.RunId IN (select RD.RunId from ECLS.RunDetails RD 
  INNER JOIN ECLS.ModeCodes MC ON RD.Mode = MC.Code AND MC.Abbreviation IN ('AVCO2R', 'VVCO2R')
  )
 AND C.CenterID = @CenterID
 AND R.SupportType = @SupportType
 AND NOT R.CompletedBy IS NULL
 AND NOT R.TimeOn IS NULL
 AND DATEDIFF(year, R.timeOn, GETDATE()) < 10
 AND DATEDIFF(day, P.Birthdate, R.timeOn) <= 30
); 

SET @Total5AllComplications = (SELECT count(*) FROM ECLS.Runs R

 INNER JOIN Registry.Patients P on R.PatientId = P.PatientId
 INNER JOIN Registry.CenterPatients CP ON CP.PatientId = P.PatientId
 INNER JOIN Registry.Centers C on C.CenterId = CP.CenterId

where 1 = 1

 AND NOT R.RunId IN (select RD.RunId from ECLS.RunDetails RD 
  INNER JOIN ECLS.ModeCodes MC ON RD.Mode = MC.Code AND MC.Abbreviation IN ('AVCO2R', 'VVCO2R')
  )
 AND C.CenterNo IN (SELECT CenterNo from Registry.Vol1_5)
 AND R.SupportType = @SupportType
 AND NOT R.CompletedBy IS NULL
 AND NOT R.TimeOn IS NULL
 AND DATEDIFF(year, R.timeOn, GETDATE()) < 10
 AND DATEDIFF(day, P.Birthdate, R.timeOn) <= 30
);

SET @Total6_10AllComplications = (SELECT count(*) FROM ECLS.Runs R

 INNER JOIN Registry.Patients P on R.PatientId = P.PatientId
 INNER JOIN Registry.CenterPatients CP ON CP.PatientId = P.PatientId
 INNER JOIN Registry.Centers C on C.CenterId = CP.CenterId

where 1 = 1

 AND NOT R.RunId IN (select RD.RunId from ECLS.RunDetails RD 
  INNER JOIN ECLS.ModeCodes MC ON RD.Mode = MC.Code AND MC.Abbreviation IN ('AVCO2R', 'VVCO2R')
  )
 AND C.CenterNo IN (SELECT CenterNo from Registry.Vol6_10)
 AND R.SupportType = @SupportType
 AND NOT R.CompletedBy IS NULL
 AND NOT R.TimeOn IS NULL
 AND DATEDIFF(year, R.timeOn, GETDATE()) < 10
 AND DATEDIFF(day, P.Birthdate, R.timeOn) <= 30
);

 SET @Total11AllComplications = (SELECT count(*) FROM ECLS.Runs R

 INNER JOIN Registry.Patients P on R.PatientId = P.PatientId
 INNER JOIN Registry.CenterPatients CP ON CP.PatientId = P.PatientId
 INNER JOIN Registry.Centers C on C.CenterId = CP.CenterId

where 1 = 1

 AND NOT R.RunId IN (select RD.RunId from ECLS.RunDetails RD 
  INNER JOIN ECLS.ModeCodes MC ON RD.Mode = MC.Code AND MC.Abbreviation IN ('AVCO2R', 'VVCO2R')
  )
 AND C.CenterNo IN (SELECT CenterNo from Registry.Vol11)
 AND R.SupportType = @SupportType
 AND NOT R.CompletedBy IS NULL
 AND NOT R.TimeOn IS NULL
 AND DATEDIFF(year, R.timeOn, GETDATE()) < 10
 AND DATEDIFF(day, P.Birthdate, R.timeOn) <= 30
);

如何计算所有这些变量

@MyCenterAllComplications,
@Total5AllComplications,
@Total6_10AllComplications,
@Total11AllComplications

同时,通过使用一个查询?提前感谢您的帮助。

【问题讨论】:

不需要 where 1 = 1,我不确定是否将所有选择合并到一个查询中,但是您可以将初始重复的选择语句拉回临时表以节省检索 4 次 请在此处查看如何改进问题:***.com/help/mcve 【参考方案1】:

您可以使用带有CASE EXPRESSION 的条件聚合将所有这些查询合并为 1:

  SELECT @MyCenterAllComplications = SUM(MyCenterAllComplications) ,
         @Total5AllComplications = SUM(Total5AllComplications),
  .......
FROM(
    SELECT CASE WHEN C.CenterID = @CenterID THEN 1 ELSE 0 END as MyCenterAllComplications , 
           CASE WHEN C.CenterNo IN (SELECT CenterNo from Registry.Vol1_5) THEN 1 ELSE 0 END as Total5AllComplications  ,
       ....
    FROM ECLS.Runs R
     INNER JOIN Registry.Patients P on R.PatientId = P.PatientId
     INNER JOIN Registry.CenterPatients CP ON CP.PatientId = P.PatientId
     INNER JOIN Registry.Centers C on C.CenterId = CP.CenterId
    where 1 = 1
     AND NOT R.RunId IN (select RD.RunId from ECLS.RunDetails RD 
      INNER JOIN ECLS.ModeCodes MC ON RD.Mode = MC.Code AND MC.Abbreviation IN ('AVCO2R', 'VVCO2R')
      )
     AND R.SupportType = @SupportType
     AND NOT R.CompletedBy IS NULL
     AND NOT R.TimeOn IS NULL
     AND DATEDIFF(year, R.timeOn, GETDATE()) < 10
     AND DATEDIFF(day, P.Birthdate, R.timeOn) <= 30)

除此之外,您应该发布此查询和表结构的解释计划,以便我们可以告诉您表中是否缺少任何索引。

【讨论】:

非常感谢。不幸的是,我收到一条错误消息:“无法对包含聚合或子查询的表达式执行聚合函数。”与@Total5AllComplications = COUNT(CASE WHEN C.CenterNo IN (SELECT CenterNo from Registry.Vol1_5) THEN 1 END) 等条件相关。 很抱歉,我在 SELECT @MyCenterAllComplications = SUM(MyCenterAllComplications) 和类似内容中有错误:“无效的列名 MyCenterAllComplications”等。 那你没有正确地给它们起别名,看看内部选择 SELECT CASE .... AS MyCenter... 。对每个变量都这样做 我做了,请看代码:SELECT CASE WHEN C.CenterID = @CenterID THEN 1 ELSE 0 END as MyCenterAllComplications , CASE WHEN C.CenterNo IN (SELECT CenterNo from Registry.Vol1_5) THEN 1 ELSE 0 END as Total5AllComplications, CASE WHEN C.CenterNo IN (SELECT CenterNo from Registry.Vol6_10) THEN 1 ELSE 0 END as Total6_10AllComplications, CASE WHEN C.CenterNo IN (SELECT CenterNo from Registry.Vol11) THEN 1 ELSE 0 END as Total11AllComplications FROM ECLS.Runs R @alenan2013 使用您正在使用的新查询更新您的帖子,以及它产生的完整错误。【参考方案2】:
SELECT count(cID.ID)
     , count(Registry.Vol1_5.CenterNo)
     , count(Registry.Vol6_10.CenterNo)
     , count(on Registry.Vol11.CenterNo)
FROM ECLS.Runs R
INNER JOIN Registry.Patients P 
        on R.PatientId = P.PatientId
       AND R.SupportType = @SupportType
       AND NOT R.CompletedBy IS NULL
       AND NOT R.TimeOn IS NULL
       AND DATEDIFF(year, R.timeOn, GETDATE()) < 10
       AND DATEDIFF(day, P.Birthdate, R.timeOn) <= 30
       AND NOT R.RunId IN (select RD.RunId 
                           from ECLS.RunDetails RD 
                           INNER JOIN ECLS.ModeCodes MC 
                            ON RD.Mode = MC.Code 
                           AND MC.Abbreviation IN ('AVCO2R', 'VVCO2R')
                          )
INNER JOIN Registry.CenterPatients CP ON CP.PatientId = P.PatientId
INNER JOIN Registry.Centers C on C.CenterId = CP.CenterId 
LEFT JOIN (select @CenterID as ID
          ) cID
             on cID.ID                   = C.CenterNo 
LEFT JOIN Registry.Vol1_5
            on Registry.Vol1_5.CenterNo  = C.CenterNo 
LEFT JOIN Registry.Vol6_10
            on Registry.Vol6_10.CenterNo = C.CenterNo 
LEFT JOIN Registry.Vol11
            on Registry.Vol11.CenterNo   = C.CenterNo 

【讨论】:

【参考方案3】:

仅仅知道你的查询是不够的,它需要知道如何构建你的数据库。

一般的响应可能是:阅读查询执行计划并检查是否需要索引: https://technet.microsoft.com/en-us/library/ms178071(v=sql.105).aspx

要查看执行计划,请单击结果窗格中的执行计划选项卡。 SQL Server Management Studio 中的图形执行计划输出是从右到左、从上到下读取的。显示分析的批次中的每个查询,包括每个查询的成本占批次总成本的百分比。

【讨论】:

非常感谢。我找到了执行计划,我会试着去理解它。

以上是关于如何优化这些 T-SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL 查询优化

需要帮助优化 T-SQL 查询

SQL Server / T-SQL:查询优化帮助

优化我的 T-SQL 查询以提高性能

优化具有 While 循环和交叉应用的 T-SQL 查询

尝试在 T-SQL 查询中使用 Levenshtein 距离 - 请帮助优化