SQL. Каково влияние производительности нескольких операторов CASE в SELECT - Teradata

Итак, у меня есть запрос, для которого требуется команда CASE-операторов в SELECT. Это был не оригинальный дизайн, а часть компромисса.

Таким образом, запрос выглядит примерно так:

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 из запроса, будет иметь большое влияние на производительность и почему?

Я тестирую это, поэтому могу понять, влияет ли производительность, но я так же интересуюсь деталями ПОЧЕМУ? (Техническая информация о том, почему)

Спасибо за вашу помощь!

Ответ 1

Операторы case будут намного меньше фактора, чем объединения в предложении WHERE.

Основным драйвером производительности в SQL является ввод-вывод - чтение данных с диска. Я думаю, что это на два порядка важнее, чем обработка в строках. Это всего лишь эвристика, не основанная на конкретных тестах в базе данных.

Вы выполняете самообучение, что потребует либо большой работы, читающей таблицу, либо большой объем работы, связанной с индексами.

С другой стороны, оператор case превращается в очень примитивные аппаратные команды - equals, gotos и т.п. Данные хранятся в памяти, ближайшей к процессорам, поэтому она будет застегиваться. Вы ничего не делаете в аргументе case (например, подобный или подзапрос). Я бы предположил, что запрос будет таким же быстрым, если вы удалите большую часть строк в инструкции.

Если у вас возникли проблемы с производительностью, введите индекс (VERS_NM, RPT_PERD_TYPE_CD, DATA_VLDTN_IND, Perd_END_RPT_DT). Этот четырехчастный индекс должен позволить вам получить максимальную дату без вызова запросов ввода-вывода в исходной таблице.

Ответ 2

Изменить: На самом деле вы можете перегруппировать оба этих подзапроса в 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)'  );

И сделайте a 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'
  )