oracle: настройка коррелированных подзапросов в предложении select - PullRequest
1 голос
/ 10 марта 2011

В Oracle 10gR2, с учетом следующего запроса, выполнение выполняется вечно.Это связано со всеми коррелированными подзапросами в предложении select.Должен быть лучший способ.Я думаю, что переписывание коррелированных подзапросов в качестве объединений поможет, или построение запроса таким образом, что потребуется только один коррелированный подзапрос, или, возможно, с использованием некоторого типа аналитической функции, но пока это вне меня.Любая помощь будет высоко ценится.

Вот запрос:

SELECT COL_1,
       TAB_1.COL_2                                 AS REPORT,
       (SELECT COL_3
        FROM   TAB_2
        WHERE  TAB_2.COL_1 = TAB_1.COL_1)          AS DEPOT,
       (SELECT COUNT(DISTINCT( TAB_3.COL_4 ))
        FROM   TAB_3
        WHERE  TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_1.COL_2 = TAB_3.COL_6
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Parts Shortage') AS P_SHORTAGES,
       (SELECT COUNT(DISTINCT( Trim(COL_10) ))
        FROM   TAB_3
        WHERE  TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_1.COL_2 = TAB_3.COL_6
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Parts Shortage') AS PARTS_AFFECTED,
       (SELECT COUNT(TAB_3.COL_7)
        FROM   TAB_3
        WHERE  TAB_3.COL_7 = 1
               AND TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_1.COL_2 = TAB_3.COL_6
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Parts Shortage') AS PARTS_CATEGORY1,
       (SELECT COUNT(TAB_3.COL_4)
        FROM   TAB_3
        WHERE  TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_3.COL_6 = TAB_1.COL_2
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Unsrv Asset')    AS U_SHORTAGES,
       (SELECT COUNT(TAB_3.COL_10)
        FROM   TAB_3
        WHERE  TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_3.COL_6 = TAB_1.COL_2
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Unsrv Asset')    AS U_AFFECTED,
       (SELECT COUNT(DISTINCT( Trim(TAB_3.COL_7) ))
        FROM   TAB_3
        WHERE  TAB_3.COL_7 = 1
               AND TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_3.COL_6 = TAB_1.COL_2
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Unsrv Asset')    AS UNSRV_CAT1,
       To_char(TAB_1.COL_11, 'MM/DD/YY')           AS REPORT_DATE
FROM   TAB_1;  

Для того, чтобы сделать вещи менее сложными, я попытался переписать только следующее, но я все еще впотеря:

    SELECT COL_1,
       (SELECT COUNT(DISTINCT( TAB_3.COL_4 ))
        FROM   TAB_3
        WHERE  TAB_3.COL_9 = TAB_1.COL_1
               AND TAB_1.COL_2 = TAB_3.COL_6
               AND TAB_3.COL_5 IS NULL
               AND TAB_3.COL_8 = 'Parts Shortage') AS P_SHORTAGES
FROM   TAB_1;  

Ответы [ 2 ]

3 голосов
/ 10 марта 2011

Это довольно загруженный вопрос. Здесь недостаточно информации (например, план выполнения, количество записей, доступные индексы, физическое расположение данных в таблице и т. Д.), Чтобы правильно настроить запрос.Лучшее, что можно сделать, - это дать предположение.Это, как говорится ... вот мое лучшее предположение:

select distinct
    tab_1.col_1,
    tab_1.col_2 AS report,
    tab_2.col_3 AS depot,
    COUNT (DISTINCT (case when tab_3.col_8 = 'Parts Shortage' then tab_3.col_4 end)) over (partition by tab_1.col_1, tab_1.col_2) AS p_shortages,
    COUNT (DISTINCT (case when tab_3.col_8 = 'Parts Shortage' then TRIM (tab_3.col_10) end)) over (partition by tab_1.col_1, tab_1.col_2)  AS parts_affected,
    COUNT (case when tab_3.col_8 = 'Parts Shortage'  and tab_3.col_7 = 1 then tab_3.col_7 end) over (partition by tab_1.col_1, tab_1.col_2)  AS parts_category1,
    COUNT (case when tab_3.col_8 = 'Unsrv Asset' then tab_3.col_4 end) over (partition by tab_1.col_1, tab_1.col_2)  AS u_shortages,
    COUNT (case when tab_3.col_8 = 'Unsrv Asset' then tab_3.col_10 end) over (partition by tab_1.col_1, tab_1.col_2)  AS u_affected,
    COUNT (DISTINCT (case when tab_3.col_8 = 'Unsrv Asset' and tab_3.col_7 = 1 then TRIM(tab_3.col_7) end)) over (partition by tab_1.col_1, tab_1.col_2)  AS unsrv_cat1,
    TO_CHAR(tab_1.col_11, 'MM/DD/YY') AS report_date
from tab_1
    left outer join tab_2
        on tab_2.col_1 = tab_1.col_1
    left outer join tab_3
        on     tab_3.col_9 = tab_1.col_1
        AND tab_3.col_6 = tab_1.col_2 
        AND tab_3.col_5 IS NULL;

После более внимательного изучения запроса выше, чтобы объяснить, что я сделал, я изменил его дальше.Это было связано с тем, что я группировал по TO_CHAR (tab_1.col_11, «MM / DD / YY»), но увидел, что это не является частью коррелированных критериев подзапроса, поэтому его пришлось изменить (если только комбинация tab_1.col_1 и col_2сами по себе уникальны).

Итак, теперь попробуем объяснить это:

По сути, в исходном запросе происходит то, что для каждой строки tab_1 вы выполняете несколько запросов на tab_3.Поэтому вместо этого я изменил его на внешнее соединение на tab_3.Поскольку я не знаю данных, это должно было быть внешнее соединение, потому что коррелированный подзапрос не удалит строки из конечного вывода, где может быть внутреннее соединение.Я только что присоединился к tab_3 один раз, так как все подзапросы объединяли tab_3 обратно к tab_1, используя те же поля.Я просто переместил конкретную логику для каждого подзапроса в инструкцию case внутри счетчика, чтобы case возвращал ноль (и, следовательно, не считался), если критерии не были выполнены.Эта новейшая версия использует агрегатные функции для получения моих вычислений на правильном уровне (tab_1 col_1 и col_2, на которых основывался исходный подзапрос).Поскольку преобразование подзапроса во внешнее объединение могло создать больше строк (если между таблицами нет совпадения 1: 1), я добавил отличительные, чтобы получить только одну строку для каждой строки в tab_1.Все строки для каждой строки в tab_1 должны быть одинаковыми.Если в tab_1 уже есть дубликаты, вам нужно будет сделать что-то более глубокое, чтобы сохранить количество записей одинаковым.

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

--------------------------- Более подробное объяснение

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

TAB_1:

col_1           col_2
--------        ---------
A               B
C               D

TAB_3:

col_9           col_6          col_4
--------        ---------      ---------
A               B              X
A               B              Y
A               B              Z
C               D              X
C               D              X

Используя метод подзапроса, вы просматриваете каждую строку tab_1 и затем выполняете запрос к tab_3.Таким образом, вы должны сделать:

для строки col_1 = A, col_2 = B, запустить счетчик выбора (отличный (col_4)) на tab_3, где col_9 = A и col_6 = B. Это возвращает значение 3, котороезначение, возвращаемое подзапросом.

для строки col_1 = C, col_2 = D, запустите счетчик выбора (отличный (col_4)) на tab_3, где col_9 = C и col_6 = D. Это возвращает значение 1, котороеэто значение, которое возвращает подзапрос.

Используя метод join, вы сначала объединяете таблицы, предоставляя вам такие данные, как:

col_1           col_2          col_9           col_6          col_4
--------        ---------      --------        ---------      ---------
A               B              A               B              X
A               B              A               B              Y
A               B              A               B              Z
C               D              C               D              X
C               D              C               D              X

Так что теперь вам просто нужно сделать запрос на основе этих данных., делая Count (отличный (col_4)) для каждого значения col_1, col_2.Если вы знаете, как выглядят ваши данные, вы можете создать более эффективный запрос, но идея остается той же.

Надеюсь, это сделает его немного более ясным!

2 голосов
/ 06 декабря 2011

Самый эффективный способ настройки связанных подзапросов - иметь правильный индекс для присоединяющихся столбцов.Поэтому я хотел бы предложить вам иметь правильные индексы для соединяющихся столбцов.

Составной индекс на Tab3 для col9, col6.

Составной индекс на Tab1 для col1 и col2.

Индекс на Tab3.col5 (если он имеет наибольшее количество нулей, иначе не создавайте его)

Индекс на основе функций для tab3.col8 create index <index_name> on tab3(upper(col8))

и изменениезапрос на использование функции UPPER скажем .. UPPER(TAB_3.COL_8) = 'PARTS SHORTAGE' ...

Не забудьте выполнить этот оператор перед выполнением запроса, в противном случае индекс на основе функций не будет полезен.

alter session set query_rewrite_enabled=true (это делает оптимизаториспользовать ФБР).

Затем выполните запрос.Проверьте план объяснения, если запрос правильно использует все индексы ... иначе добавьте / * + Index () * /, чтобы правильно использовать индексы в запросе.

...