所以我有一个查询需要SELECT中的一堆CASE语句。这不是原始设计,而是妥协的一部分。
所以查询看起来像这样:
SELECT
CONT.TABLE.FINC_ACCT_NM,
CONT.TABLE.FINC_ACCT_ID,
CONT.TABLE.CURR_END_OF_PERD_ACTL_VAL,
CONT.TABLE.PREV_END_OF_PERD_ACTL_VAL,
CONT.TABLE.VARNC_PLAN_VAL,
CONT.TABLE.OUTLOOK_BDGT_PLAN_VAL,
CONT.TABLE.PERD_END_RPT_DT,
CONT.TABLE.PLAN_VERS_NM,
CONT.TABLE.FRMT_ACTL_CD,
CONT.TABLE.FRMT_PLAN_CD,
CONT.TABLE.RPT_PERD_TYPE_CD,
CASE
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Net Interest Income'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Non Interest Income'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Non-Interest Expense'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Total Marketing Expense'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Total Operating Expense'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Pre-Provision Earnings (before tax)'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Net Charge-offs'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Other'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then ' Allowance Build (Release)'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Provision Expense'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Pretax Income'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Tax Expense'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'NIAT'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'EPS'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Ending Loans - HFI'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'avg' then 'Average Loans - HFI'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'avg' then 'Average Earning Assets'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Ending Deposits'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'avg' then 'Average Deposits'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'NIM on Loans'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Revenue Margin'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'AC579' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Charge off rate'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Efficiency ratio'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'ROA'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'ROE'
WHEN ( CONT.TABLE.FINC_ACCT_ID )= 'XXXX' and ( CONT.TABLE.BAL_TYPE_CD ) = 'EOP' then 'Return on Allocated Capital (ROAC)'
ELSE ( CONT.TABLE.FINC_ACCT_NM ) end
FROM
CONT.TABLE
WHERE
(
(
( ( CONT.TABLE.PERD_END_RPT_DT ) = (
SELECT Max(Perd_END_RPT_DT)
FROM CONT.TABLE
Where VERS_NM='Actual'
AND RPT_PERD_TYPE_CD = 'Q'
AND DATA_VLDTN_IND='Y'
)
AND RPT_PERD_TYPE_CD = 'Q'
AND DATA_VLDTN_IND='Y' )
OR
( ( CONT.TABLE.PERD_END_RPT_DT ) = (
SELECT Max(Perd_END_RPT_DT)
FROM CONT.TABLE
Where VERS_NM='Actual'
AND RPT_PERD_TYPE_CD = 'M'
AND DATA_VLDTN_IND='Y'
)
AND RPT_PERD_TYPE_CD = 'M'
AND DATA_VLDTN_IND='Y' )
)
AND
( ( CONT.TABLE.DATA_VLDTN_IND )='Y' )
AND
( ( CONT.TABLE.FINC_ACCT_ID )IN ('AC0006470','AC8000199','AC8002145','AC0006586','AC8000094') AND ( CONT.TABLE.DEPT_ID )='OR80637' )
)
我的问题是,将所有CASE语句更改为直接列引用对性能有何影响。
换句话说:如果我将每个CASE语句更改为只是一个列名并从查询中删除所有CASE语句会对性能产生很大影响,为什么会这样?
我正在测试这个,所以我可以弄清楚性能是否受到影响但我对WHY的细节感兴趣? (技术细节为何)
感谢您的帮助!
答案 0 :(得分:20)
case语句比WHERE子句中的连接要少得多。
SQL中性能的主要驱动因素是I / O--从磁盘读取数据。我认为它比行中的处理更重要两个数量级。这只是一种启发式方法,而不是基于对数据库的特定测试。
您正在进行自我加入,这需要大量的工作来阅读表格或者需要处理索引的大量工作。
另一方面,case语句变成了非常原始的硬件命令 - equals,gotos等。数据驻留在最靠近处理器的内存中,因此它将进行压缩。你在case语句中没有做任何事情(例如like或子查询)。我想如果你删除了语句中的大部分行,查询速度会一样快。
如果您遇到性能问题,请在(VERS_NM,RPT_PERD_TYPE_CD,DATA_VLDTN_IND,Perd_END_RPT_DT)上添加索引。这个由四部分组成的索引应该允许您在不调用原始表的I / O请求的情况下获取最大日期。
答案 1 :(得分:8)
编辑:实际上,您可以将这两个子查询重新计算为JOIN
,无论如何这可能会更快。它也消除了很多重复!
这真的不是关于查询的性能(@Gordon已经很好地介绍了),但是这个巨大的案例陈述似乎只是一个维护噩梦。也许更好的方法是将其转换为表格
CREATE TABLE ACCT_DISPLAY_NAME (
FINC_ACCT_ID CHAR(10),
BAL_TYPE_CD CHAR(3),
DISPLAY_NAME VARCHAR(100)
);
CREATE INDEX ACCT_DISPLAY_INDEX ON ACCT_DISPLAY_NAME (
FINC_ACCT_ID,
BAL_TYPE_CD
);
INSERT INTO ACCT_DISPLAY_NAME VALUES
('AC99800' , 'EOP', ' Net Interest Income' ),
('AC12993' , 'EOP', ' Non Interest Income' ),
('AC667999' , 'EOP', 'Non-Interest Expense' ),
('AC996587' , 'EOP', ' Total Marketing Expense' ),
('AC659986' , 'EOP', ' Total Operating Expense' ),
('AC69678' , 'EOP', 'Pre-Provision Earnings (before tax)' ),
('AC09994' , 'EOP', ' Net Charge-offs' ),
('AC20977' , 'EOP', ' Other' ),
('AC19979' , 'EOP', ' Allowance Build (Release)' ),
('AC7094' , 'EOP', 'Provision Expense' ),
('AC6997' , 'EOP', 'Pretax Income' ),
('AC0994' , 'EOP', 'Tax Expense' ),
('AC9999' , 'EOP', 'NIAT' ),
('AC7990' , 'EOP', 'EPS' ),
('AC9995' , 'EOP', 'Ending Loans - HFI' ),
('AC9995' , 'avg', 'Average Loans - HFI' ),
('AC2991' , 'avg', 'Average Earning Assets' ),
('AC2999' , 'EOP', 'Ending Deposits' ),
('AC9999' , 'avg', 'Average Deposits' ),
('AC0379' , 'EOP', 'NIM on Loans' ),
('AC6999' , 'EOP', 'Revenue Margin' ),
('AC579' , 'EOP', 'Charge off rate' ),
('AC5899' , 'EOP', 'Efficiency ratio' ),
('AC629' , 'EOP', 'ROA' ),
('AC359' , 'EOP', 'ROE' ),
('AC619' , 'EOP', 'Return on Allocated Capital (ROAC)' );
并对其进行LEFT JOIN
(因为ELSE
中有CASE
),例如:
SELECT T.FINC_ACCT_NM,
T.FINC_ACCT_ID,
T.CURR_END_OF_PERD_ACTL_VAL,
T.PREV_END_OF_PERD_ACTL_VAL,
T.VARNC_PLAN_VAL,
T.OUTLOOK_BDGT_PLAN_VAL,
T.PERD_END_RPT_DT,
T.PLAN_VERS_NM,
T.FRMT_ACTL_CD,
T.FRMT_PLAN_CD,
T.RPT_PERD_TYPE_CD,
COALESCE(N.DISPLAY_NAME, T.FINC_ACCT_NM)
FROM CONT.TABLE T
JOIN (
SELECT RPT_PERD_TYPE_CD, DATA_VLDTN_IND, Max(Perd_END_RPT_DT) AS PERD_END_RPT_DT
FROM CONT.TABLE
WHERE VERS_NM='Actual'
AND DATA_VLDTN_IND='Y'
GROUP BY RPT_PERD_TYPE_CD, DATA_VLDTN_IND
) AS MAX_DATES
ON T.RPT_PERD_TYPE_CD = MAX_DATES.RPT_PERD_TYPE_CD
AND T.DATA_VLDTN_IND = MAX_DATES.DATA_VLDTN_IND
AND T.PERD_END_RPT_DT = MAX_DATES.PERD_END_RPT_DT
LEFT JOIN ACCT_DISPLAY_NAME N
ON T.FINC_ACCT_ID = N.FINC_ACCT_ID
AND T.BAL_TYPE_CD = N.BAL_TYPE_CD
WHERE T.DEPT_ID = 'OR80637'
AND T.RPT_PERD_TYPE_CD IN ('Q', 'M')
AND T.FINC_ACCT_ID IN (
'AC0006470',
'AC8000199',
'AC8002145',
'AC0006586',
'AC8000094'
)