Как я могу улучшить производительность запросов с оконными функциями, которые, кажется, игнорируют индексы? - PullRequest
1 голос
/ 11 декабря 2019

Как я могу улучшить производительность запросов с оконными функциями, которые, кажется, игнорируют индексы?
Или есть ли дополнительные индексы, которые мне нужно создать?
Или я могу исключить самостоятельные объединения из моего окончательного запроса?

У меня есть запрос с оконными функциями, и мне нужно, чтобы он работал гладко.
Я мог бы удалить оконные функции и переключиться на GROUP BY - но я думаю, что это будет медленнее?

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

Базовая таблица имеет индекс, определенный для каждого столбца, на который есть ссылка.
Но когда я EXPLAIN запрашиваю этот запрос, он не использует ни один из индексов, и peВместо этого выполняется полное сканирование таблицы.

В настоящее время в таблице 28 000 строк, но со временем она будет увеличиваться (около 10 000 строк в день).

EDGE_VP, EDGE_RM иEDGE_ASM столбцы содержат адрес электронной почты - строковые функции удаляют домен.

SELECT DISTINCT SS_TIMESTAMP,
                CASE WHEN INSTR(EDGE_VP,'@oracle.com')=0 THEN EDGE_VP ELSE SUBSTR(EDGE_VP,1,INSTR(EDGE_VP,'@oracle.com')-1) END AS EDGE_VP,
                CASE WHEN INSTR(EDGE_RM,'@oracle.com')=0 THEN EDGE_RM ELSE SUBSTR(EDGE_RM,1,INSTR(EDGE_RM,'@oracle.com')-1) END AS EDGE_RM,
                CASE WHEN INSTR(EDGE_ASM,'@oracle.com')=0 THEN EDGE_ASM ELSE SUBSTR(EDGE_ASM,1,INSTR(EDGE_ASM,'@oracle.com')-1) END AS EDGE_ASM,
                NVL(SUM(CASE WHEN OPPTY_STATUS = 'Open' THEN ARR_PIPELINE END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS PIPELINE,
                NVL(SUM(CASE WHEN OPPTY_STATUS = 'Open' THEN ARR_BEST END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS BEST,
                NVL(SUM(CASE WHEN OPPTY_STATUS = 'Open' THEN ARR_FORECAST END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS FORECAST,
                NVL(SUM(CASE WHEN OPPTY_STATUS = 'Won' THEN ARR END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS CLOSED,
                COUNT(*) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM) AS ROW_COUNT
FROM SS_EDGE_FORECAST
WHERE EDGE_ASM NOT IN('Email_Address1', 'Email_Address2')

ЩЕЛКНИТЕ ИЗОБРАЖЕНИЯ ДЛЯ УВЕЛИЧЕНИЯ:

enter image description here

enter image description here

Вот расширенный запрос, который я использую позже.
Обратите внимание на запрос вверху, в WITH предложение.
В конце этого запроса я в настоящее время использую самостоятельные объединения запроса WITH - может быть, я должен использовать здесь и оконные функции?

Могу ли яне говорю вам, сколько времени это займет, чтобы выполнить, потому что он зависает, когда я пытаюсь запустить его, и у меня нет терпения ждать часами и т. д.

CREATE OR REPLACE FORCE VIEW "EDGE_FORECAST_OVER_TIME" AS
WITH basequery AS (SELECT DISTINCT SS_TIMESTAMP,
                                   CASE WHEN INSTR(EDGE_VP,'@oracle.com')=0 THEN EDGE_VP ELSE SUBSTR(EDGE_VP,1,INSTR(EDGE_VP,'@oracle.com')-1) END AS EDGE_VP,
                                   CASE WHEN INSTR(EDGE_RM,'@oracle.com')=0 THEN EDGE_RM ELSE SUBSTR(EDGE_RM,1,INSTR(EDGE_RM,'@oracle.com')-1) END AS EDGE_RM,
                                   CASE WHEN INSTR(EDGE_ASM,'@oracle.com')=0 THEN EDGE_ASM ELSE SUBSTR(EDGE_ASM,1,INSTR(EDGE_ASM,'@oracle.com')-1) END AS EDGE_ASM,
                                   NVL(SUM(CASE WHEN OPPTY_STATUS = 'Open' THEN ARR_PIPELINE END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS PIPELINE,
                                   NVL(SUM(CASE WHEN OPPTY_STATUS = 'Open' THEN ARR_BEST END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS BEST,
                                   NVL(SUM(CASE WHEN OPPTY_STATUS = 'Open' THEN ARR_FORECAST END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS FORECAST,
                                   NVL(SUM(CASE WHEN OPPTY_STATUS = 'Won' THEN ARR END) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM),0) AS CLOSED,
                                   COUNT(*) OVER (PARTITION BY SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM) AS ROW_COUNT
                   FROM SS_EDGE_FORECAST
                   WHERE EDGE_ASM NOT IN('Email_Address1', 'Email_Address2'))
SELECT ss.TIMESTAMP,
       ss.TIMESTAMP_DATE,
       ss.FREQUENCY,
       ss.PREV_TIMESTAMP,
       ss.PREV_F_TIMESTAMP,
       ss.PREV_H_TIMESTAMP,
       ss.PREV_D_TIMESTAMP,
       ss.PREV_W_TIMESTAMP,
       ss.PREV_M_TIMESTAMP,
       ss.PREV_Q_TIMESTAMP,
       ss.PREV_Y_TIMESTAMP,
       ss.PREV_TIMESTAMP_DATE,
       ss.PREV_F_TIMESTAMP_DATE,
       ss.PREV_H_TIMESTAMP_DATE,
       ss.PREV_D_TIMESTAMP_DATE,
       ss.PREV_W_TIMESTAMP_DATE,
       ss.PREV_M_TIMESTAMP_DATE,
       ss.PREV_Q_TIMESTAMP_DATE,
       ss.PREV_Y_TIMESTAMP_DATE,
       ss.DAYS_SINCE_PREV_TIMESTAMP,
       ss.DAYS_SINCE_PREV_F_TIMESTAMP,
       ss.DAYS_SINCE_PREV_H_TIMESTAMP,
       ss.DAYS_SINCE_PREV_D_TIMESTAMP,
       ss.DAYS_SINCE_PREV_W_TIMESTAMP,
       ss.DAYS_SINCE_PREV_M_TIMESTAMP,
       ss.DAYS_SINCE_PREV_Q_TIMESTAMP,
       ss.DAYS_SINCE_PREV_Y_TIMESTAMP,
       ss.DAYS_SINCE_PREV_TS_DATE,
       ss.DAYS_SINCE_PREV_F_TS_DATE,
       ss.DAYS_SINCE_PREV_H_TS_DATE,
       ss.DAYS_SINCE_PREV_D_TS_DATE,
       ss.DAYS_SINCE_PREV_W_TS_DATE,
       ss.DAYS_SINCE_PREV_M_TS_DATE,
       ss.DAYS_SINCE_PREV_Q_TS_DATE,
       ss.DAYS_SINCE_PREV_Y_TS_DATE,

       bq.EDGE_VP,
       bq.EDGE_RM,
       bq.EDGE_ASM,

       bq.PIPELINE,
       bq.BEST,
       bq.FORECAST,
       bq.CLOSED,
       bq.PIPELINE + bq.BEST AS PIPE_BEST,
       bq.CLOSED + bq.FORECAST AS CLOSED_FORECAST,

       bqp.PIPELINE AS PREV_PIPELINE,
       bqp.BEST AS PREV_BEST,
       bqp.FORECAST AS PREV_FORECAST,
       bqp.CLOSED AS PREV_CLOSED,
       bqp.PIPELINE + bqp.BEST AS PREV_PIPE_BEST,
       bqp.CLOSED + bqp.FORECAST AS PREV_CLOSED_FORECAST,
       bq.PIPELINE - bqp.PIPELINE AS PIPELINE_DIFF,
       bq.BEST - bqp.BEST AS BEST_DIFF,
       bq.FORECAST - bqp.FORECAST AS FORECAST_DIFF,
       bq.CLOSED - bqp.CLOSED AS CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqp.PIPELINE + bqp.BEST) AS PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqp.CLOSED + bqp.FORECAST) AS CLOSED_FORECAST_DIFF,

       bqpf.PIPELINE AS PREV_F_PIPELINE,
       bqpf.BEST AS PREV_F_BEST,
       bqpf.FORECAST AS PREV_F_FORECAST,
       bqpf.CLOSED AS PREV_F_CLOSED,
       bqpf.PIPELINE + bqpf.BEST AS PREV_F_PIPE_BEST,
       bqpf.CLOSED + bqpf.FORECAST AS PREV_F_CLOSED_FORECAST,
       bq.PIPELINE - bqpf.PIPELINE AS F_PIPELINE_DIFF,
       bq.BEST - bqpf.BEST AS F_BEST_DIFF,
       bq.FORECAST - bqpf.FORECAST AS F_FORECAST_DIFF,
       bq.CLOSED - bqpf.CLOSED AS F_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqpf.PIPELINE + bqpf.BEST) AS F_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqpf.CLOSED + bqpf.FORECAST) AS F_CLOSED_FORECAST_DIFF,

       bqph.PIPELINE AS PREV_H_PIPELINE,
       bqph.BEST AS PREV_H_BEST,
       bqph.FORECAST AS PREV_H_FORECAST,
       bqph.CLOSED AS PREV_H_CLOSED,
       bqph.PIPELINE + bqph.BEST AS PREV_H_PIPE_BEST,
       bqph.CLOSED + bqph.FORECAST AS PREV_H_CLOSED_FORECAST,
       bq.PIPELINE - bqph.PIPELINE AS H_PIPELINE_DIFF,
       bq.BEST - bqph.BEST AS H_BEST_DIFF,
       bq.FORECAST - bqph.FORECAST AS H_FORECAST_DIFF,
       bq.CLOSED - bqph.CLOSED AS H_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqph.PIPELINE + bqph.BEST) AS H_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqph.CLOSED + bqph.FORECAST) AS H_CLOSED_FORECAST_DIFF,

       bqpd.PIPELINE AS PREV_D_PIPELINE,
       bqpd.BEST AS PREV_D_BEST,
       bqpd.FORECAST AS PREV_D_FORECAST,
       bqpd.CLOSED AS PREV_D_CLOSED,
       bqpd.PIPELINE + bqpd.BEST AS PREV_D_PIPE_BEST,
       bqpd.CLOSED + bqpd.FORECAST AS PREV_D_CLOSED_FORECAST,
       bq.PIPELINE - bqpd.PIPELINE AS D_PIPELINE_DIFF,
       bq.BEST - bqpd.BEST AS D_BEST_DIFF,
       bq.FORECAST - bqpd.FORECAST AS D_FORECAST_DIFF,
       bq.CLOSED - bqpd.CLOSED AS D_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqpd.PIPELINE + bqpd.BEST) AS D_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqpd.CLOSED + bqpd.FORECAST) AS D_CLOSED_FORECAST_DIFF,

       bqpw.PIPELINE AS PREV_W_PIPELINE,
       bqpw.BEST AS PREV_W_BEST,
       bqpw.FORECAST AS PREV_W_FORECAST,
       bqpw.CLOSED AS PREV_W_CLOSED,
       bqpw.PIPELINE + bqpw.BEST AS PREV_W_PIPE_BEST,
       bqpw.CLOSED + bqpw.FORECAST AS PREV_W_CLOSED_FORECAST,
       bq.PIPELINE - bqpw.PIPELINE AS W_PIPELINE_DIFF,
       bq.BEST - bqpw.BEST AS W_BEST_DIFF,
       bq.FORECAST - bqpw.FORECAST AS W_FORECAST_DIFF,
       bq.CLOSED - bqpw.CLOSED AS W_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqpw.PIPELINE + bqpw.BEST) AS W_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqpw.CLOSED + bqpw.FORECAST) AS W_CLOSED_FORECAST_DIFF,

       bqpm.PIPELINE AS PREV_M_PIPELINE,
       bqpm.BEST AS PREV_M_BEST,
       bqpm.FORECAST AS PREV_M_FORECAST,
       bqpm.CLOSED AS PREV_M_CLOSED,
       bqpm.PIPELINE + bqpm.BEST AS PREV_M_PIPE_BEST,
       bqpm.CLOSED + bqpm.FORECAST AS PREV_M_CLOSED_FORECAST,
       bq.PIPELINE - bqpm.PIPELINE AS M_PIPELINE_DIFF,
       bq.BEST - bqpm.BEST AS M_BEST_DIFF,
       bq.FORECAST - bqpm.FORECAST AS M_FORECAST_DIFF,
       bq.CLOSED - bqpm.CLOSED AS M_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqpm.PIPELINE + bqpm.BEST) AS M_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqpm.CLOSED + bqpm.FORECAST) AS M_CLOSED_FORECAST_DIFF,

       bqpq.PIPELINE AS PREV_Q_PIPELINE,
       bqpq.BEST AS PREV_Q_BEST,
       bqpq.FORECAST AS PREV_Q_FORECAST,
       bqpq.CLOSED AS PREV_Q_CLOSED,
       bqpq.PIPELINE + bqpq.BEST AS PREV_Q_PIPE_BEST,
       bqpq.CLOSED + bqpq.FORECAST AS PREV_Q_CLOSED_FORECAST,
       bq.PIPELINE - bqpq.PIPELINE AS Q_PIPELINE_DIFF,
       bq.BEST - bqpq.BEST AS Q_BEST_DIFF,
       bq.FORECAST - bqpq.FORECAST AS Q_FORECAST_DIFF,
       bq.CLOSED - bqpq.CLOSED AS Q_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqpq.PIPELINE + bqpq.BEST) AS Q_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqpq.CLOSED + bqpq.FORECAST) AS Q_CLOSED_FORECAST_DIFF,

       bqpy.PIPELINE AS PREV_Y_PIPELINE,
       bqpy.BEST AS PREV_Y_BEST,
       bqpy.FORECAST AS PREV_Y_FORECAST,
       bqpy.CLOSED AS PREV_Y_CLOSED,
       bqpy.PIPELINE + bqpy.BEST AS PREV_Y_PIPE_BEST,
       bqpy.CLOSED + bqpy.FORECAST AS PREV_Y_CLOSED_FORECAST,
       bq.PIPELINE - bqpy.PIPELINE AS Y_PIPELINE_DIFF,
       bq.BEST - bqpy.BEST AS Y_BEST_DIFF,
       bq.FORECAST - bqpy.FORECAST AS Y_FORECAST_DIFF,
       bq.CLOSED - bqpy.CLOSED AS Y_CLOSED_DIFF,
       (bq.PIPELINE + bq.BEST) - (bqpy.PIPELINE + bqpy.BEST) AS Y_PIPE_BEST_DIFF,
       (bq.CLOSED + bq.FORECAST) - (bqpy.CLOSED + bqpy.FORECAST) AS Y_CLOSED_FORECAST_DIFF,

       bq.ROW_COUNT,
       bqp.ROW_COUNT AS PREV_ROW_COUNT,
       bq.ROW_COUNT - bqp.ROW_COUNT AS NET_ROWS_ADDED

FROM basequery bq
LEFT JOIN SNAPSHOTS ss ON ss.TIMESTAMP = bq.SS_TIMESTAMP AND ss.TABLE_NAME = 'EDGE_FORECAST'

LEFT JOIN basequery bqp ON bqp.SS_TIMESTAMP = ss.PREV_TIMESTAMP
                       AND bqp.EDGE_VP = bq.EDGE_VP
                       AND bqp.EDGE_RM = bq.EDGE_RM
                       AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpf ON bqp.SS_TIMESTAMP = ss.PREV_F_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqph ON bqp.SS_TIMESTAMP = ss.PREV_H_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpd ON bqp.SS_TIMESTAMP = ss.PREV_D_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpw ON bqp.SS_TIMESTAMP = ss.PREV_W_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpm ON bqp.SS_TIMESTAMP = ss.PREV_M_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpq ON bqp.SS_TIMESTAMP = ss.PREV_Q_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpy ON bqp.SS_TIMESTAMP = ss.PREV_Y_TIMESTAMP
                        AND bqp.EDGE_VP = bq.EDGE_VP
                        AND bqp.EDGE_RM = bq.EDGE_RM
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

ORDER BY ss.TIMESTAMP DESC,
         bq.EDGE_VP ASC,
         bq.EDGE_RM ASC,
         bq.EDGE_ASM ASC

Ответы [ 3 ]

3 голосов
/ 11 декабря 2019

Для первого запроса (select distinct) требуется индекс: SS_EDGE_FORECAST(SS_TIMESTAMP, EDGE_VP, EDGE_RM, EDGE_ASM). Это должно помочь с аналитической функцией. Четыре отдельных столбца не окажут особой помощи.

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

1 голос
/ 11 декабря 2019

Как писал @Gordon, наличие индекса для каждого столбца имеет большой смысл в вашем случае. Oracle не будет использовать более одного индекса для доступа к таблице (за исключением случаев, когда у вас есть несколько предикатов OR). Таким образом, вы можете иметь многостолбцовый индекс.

Также некоторые индексы использовать нельзя - например, потому что значения NULL не индексируются в индексах с одним столбцом.

Попробуйте использовать:

alter session set index_cost_adj=1;

Это уменьшит стоимость использования индекса в вашей сессии. Если индекс не используется в таком случае, то он, вероятно, не может быть использован .

0 голосов
/ 11 декабря 2019

Я решил это следующим образом:

a) Перемещение меньшего подзапроса в материализованное представление с ограничениями и индексами. (Это операция создания отчетов по снимку хранилища данных, и таблица обновляется ежедневно в 2:00, поэтому мне не нужно беспокоиться о постоянной «перестройке» производительности в течение дня.)

b) ИСПРАВЛЕНИЕ СБОРА КОПИИ / ПАСТЫ В МОИХ ЛЕВЫХ СОЕДИНЕНИЯХ
Боже мой ... когда я, наконец, заметил это упущение, я хотел ударить себя по лицу.

LEFT JOIN basequery bqp ON bqp.SS_TIMESTAMP = ss.PREV_TIMESTAMP
                       AND bqp.EDGE_VP = bq.EDGE_VP
                       AND bqp.EDGE_RM = bq.EDGE_RM
                       AND bqp.EDGE_ASM = bq.EDGE_ASM

LEFT JOIN basequery bqpf ON bqp.SS_TIMESTAMP = ss.PREV_F_TIMESTAMP -- problem here
                        AND bqp.EDGE_VP = bq.EDGE_VP               -- should be
                        AND bqp.EDGE_RM = bq.EDGE_RM               -- bqpf.EDGE_RM etc.
                        AND bqp.EDGE_ASM = bq.EDGE_ASM

Каждый из нихиз моей дюжины или около того левых соединений возникла эта проблема.
Неудивительно, что сервер задыхался от этого запроса при каждом его тестировании.

После исправления опечаток в левом соединении и усиления некоторыми полезными материализованными представлениямизапрос выполняется за 0,1 секунды, что для меня очень интересно, учитывая, насколько тяжелым будет этот запрос и насколько он будет полезен.

...