План коррелируемого подзапроса

Access Violation, некорректное выполнение запросов или вызовов API, ошибки утилит командной строки, в общем все, что вам мешает работать

Модераторы: kdv, dimitr

Ответить
CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

План коррелируемого подзапроса

Сообщение CyberMax » 11 апр 2008, 08:30

FB 2.1 RC2.
Запрос:

Код: Выделить всё

UPDATE PERIOD_ACCOUNT PA
SET
    PA.CHARGE_FULL =
        (SELECT
            SUM(C.AMOUNT_FULL)
         FROM
             BATCH AS B
             INNER JOIN CHARGE AS C ON B.ID = C.ID_BATCH
         WHERE
             (C.ID_ABONENT = :ID_ABONENT) AND (B.ID_PERIOD_ACCOUNT = PA.ID_PERIOD))
WHERE
    PA.ID = :ID_PERIOD_ACCOUNT;
План:
PLAN JOIN (B INDEX (FK_BATCH_PERIOD_A), F INDEX (FK_CHARGE_BATCH, FK_CHARGE_ABONENT))

Если же последнее условие заменить на
(B.ID_PERIOD_ACCOUNT = :ID_PERIOD)
то получаем план:
PLAN JOIN (F INDEX (FK_CHARGE_ABONENT), B INDEX (PK_BATCH))
который выполняется более быстрым.

Селективность FK_CHARGE_ABONENT = 0,00007901..., FK_BATCH_PERIOD_A = 0,022222...

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 11 апр 2008, 11:52

А что есть :ID_PERIOD ? Его в исходном запросе нет

CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

Сообщение CyberMax » 11 апр 2008, 14:59

Данный запрос используется внутри ХП, а ID_PERIOD, соответственно, локальная переменная.

Код: Выделить всё

FOR SELECT
     T1.ID_PERIOD,
    ...
FROM
     TABLE1 T1
INTO
    :ID_PERIOD,
    ...
DO
    UPDATE PERIOD_ACCOUNT PA
        ...
Там был вариант - либо ссылаться на :ID_PERIOD либо ссылаться на поле этой же таблицы. Сейчас ХП переписана без FOR SELECT, и для использования FK_CHARGE_ABONENT сделано "AND (B.ID_PERIOD_ACCOUNT + 0 = PA.ID_PERIOD)"

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 11 апр 2008, 18:08

вместо таблицы С в планах почему-то F, кол-во записей в таблицах не указано. Аффтар, что ты от нас хочешь? :-)

CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

Сообщение CyberMax » 12 апр 2008, 16:29

Прощу прощения. Заменил в запросе имена таблиц и альясов на другие, а планы не переименовал.
Изменил пример на через SELECT - для наглядности.

Таблица BATCH - около 5500 записей.
Таблица CHARGE - около 120 000.
Таблица PERIOD_ACCOUNT - около 1 000 000 записей.

Код: Выделить всё

SELECT
    (SELECT
        SUM(C.AMOUNT_FULL)
    FROM
        BATCH AS B
        INNER JOIN CHARGE AS C ON B.ID = C.ID_BATCH
    WHERE
        (C.ID_ABONENT = :ID_ABONENT) AND (B.ID_PERIOD = PA.ID_PERIOD))
FROM
    PERIOD_ACCOUNT PA
WHERE
    PA.ID_PERIOD = :ID_PERIOD
Получаем план
PLAN JOIN (B INDEX (FK_BATCH_PERIOD), C INDEX (FK_CHARGE_BATCH, FK_CHARGE_ABONENT))
PLAN (PA INDEX (FK_PERIOD_ACCOUNT_PERIOD))

FK_BATCH_PERIOD - индекс по BATCH.ID_PERIOD. Селективность - 0,022222.
FK_CHARGE_BATCH - индекс по CHARGE.ID_BATCH. Селективность - 0,000184.
FK_CHARGE_ABONENT - индекс по CHARGE.ID_ABONENT. Селективность 0,000079.

Запрос:

Код: Выделить всё

SELECT
    (SELECT
        SUM(C.AMOUNT_FULL)
    FROM
        BATCH AS B
        INNER JOIN CHARGE AS C ON B.ID = C.ID_BATCH
    WHERE
        (C.ID_ABONENT = :ID_ABONENT) AND (B.ID_PERIOD = :ID_PERIOD))
FROM
    PERIOD_ACCOUNT PA
WHERE
    PA.ID_PERIOD = :ID_PERIOD
План:
PLAN JOIN (C INDEX (FK_CHARGE_ABONENT), B INDEX (PK_BATCH))
PLAN (PA INDEX (FK_PERIOD_ACCOUNT_PERIOD))

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

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 13 апр 2008, 18:57

если на коленке прикинуть стоимость обоих вариантов, то второй должен быть на порядок дешевле. Соотв-но, я вижу только одно объяснение: зависимость от внешней таблицы заставляет выставить "B" первым потоком джойна независимо от стоимости. Насколько я помню, такая эвристика есть в оптимизаторе, но она рассчитана на порядок потоков внутри одного джойна, а тут косвенно влияет таблица из совсем другого контекста. Непорядок. При возможности попробую разобраться.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 14 апр 2008, 12:41

Так это ж совершенно разные запросы.
Во втором запросе вложенный один раз выполняется, вроде бы?

Я честно говоря не понял, как тут в коррелирующем случае можно по-другому план построить.
Разве можно выполнить подзапрос, а уже потом присоединять его к внешнему?

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 14 апр 2008, 13:17

даже в коррелирующем случае возможны два варианта джойна с разным порядком потоков

Merlin
Динозавр IB/FB
Сообщения: 1502
Зарегистрирован: 27 окт 2004, 11:44

Сообщение Merlin » 14 апр 2008, 13:28

И задача оптимизатора - выбрать наихудший :-D Прости, наболело :)

Ответить