嵌套CASE的T-SQL替代方案可以获得更好的性能吗?

时间:2015-05-13 15:14:29

标签: sql-server tsql nested case sql-tuning

我的T-SQL查询执行得非常糟糕,以至于超时。罪魁祸首是这两个带嵌入式查询的嵌套CASE语句:

SELECT
  CASE
    WHEN b.month_type = (CASE
        WHEN dbo.CURRENT_BUSINESSDAY(GETDATE()) >= 8 THEN 'Current Month BD2'
        ELSE (CASE
            WHEN dbo.CURRENT_BUSINESSDAY(GETDATE()) < 8 AND
              (SELECT
                MAX(b.cal_start_date)
              FROM factbillingcollectionhistory a
              JOIN dimdateperiod b
                ON a.fiscal_month = b.fsc_period)
              <> (SELECT
                MAX(cal_start_date)
              FROM dimdateperiod) THEN 'Current Reporting Month'
            ELSE 'Current Month BD2'
          END)
      END) THEN a.BILLINGS_BUDGET
    ELSE 0
  END
  AS BILLINGS_BUDGET,
  CASE
    WHEN b.month_type = (CASE
        WHEN dbo.CURRENT_BUSINESSDAY(GETDATE()) >= 8 THEN 'Current Month BD2'
        ELSE (CASE
            WHEN dbo.CURRENT_BUSINESSDAY(GETDATE()) < 8 AND
              (SELECT
                MAX(b.cal_start_date)
              FROM factbillingcollectionhistory a
              JOIN dimdateperiod b
                ON a.fiscal_month = b.fsc_period)
              <> (SELECT
                MAX(cal_start_date)
              FROM dimdateperiod) THEN 'Current Reporting Month'
            ELSE 'Current Month BD2'
          END)
      END) THEN a.COLLECTION_GOALS
    ELSE 0
  END
  AS COLLECTION_GOALS

CURRENT_BUSINESSDAY函数正如其描述的那样...识别报告期间的当前工作日。

CASE的逻辑是根据我们在报告周期中的位置以及我们是否收到更新的目标文件来返回目标值。如果它还不是BD8,请检查我们是否收到了新文件(通过比较最大日期)。如果我们已收到它,请将该值返回到报表,否则返回上个月的值。如果它在BD8之后并且我们仍然没有新文件,它应该返回“0”,这将使我们的过程失败并告诉我们他们没有按时提供数据。

是否有更有效的方法来编写此逻辑,以防止查询超时?请记住,此脚本用于在表格模型中构建表,因此只有SELECT正在运行...没有变量声明或任何类型。

思想?

1 个答案:

答案 0 :(得分:0)

由于COLLECTION_GOALS和BILLINGS_BUDGET中的大小写逻辑相同,我建议将逻辑移出内联子查询并移入主FROM子句,如下所示:

SELECT CASE WHEN b.month_type = z.month_type
           THEN a.BILLINGS_BUDGET
           ELSE 0
       END AS BILLINGS_BUDGET,
       CASE WHEN b.month_type = z.month_type
           THEN a.COLLECTION_GOALS
           ELSE 0
       END AS COLLECTION_GOALS
FROM (SELECT CASE WHEN dbo.CURRENT_BUSINESSDAY(GETDATE()) >= 8
                 THEN 'Current Month BD2'
                 ELSE (CASE WHEN dbo.CURRENT_BUSINESSDAY(GETDATE()) < 8 AND 
                                 (SELECT max(b.cal_start_date) 
                                         FROM factbillingcollectionhistory a
                                         JOIN dimdateperiod b
                                           ON a.fiscal_month = b.fsc_period) <> (SELECT max(cal_start_date) FROM dimdateperiod)
                           THEN 'Current Reporting Month'
                           ELSE 'Current Month BD2'
                       END)
             END month_type) z
CROSS JOIN
/*... Rest of query */

这应该导致每个查询评估一次,而不是查询返回的每一行两次。