Страница 1 из 1
План коррелируемого подзапроса
Добавлено: 11 апр 2008, 08:30
CyberMax
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...
Добавлено: 11 апр 2008, 11:52
hvlad
А что есть :ID_PERIOD ? Его в исходном запросе нет
Добавлено: 11 апр 2008, 14:59
CyberMax
Данный запрос используется внутри ХП, а 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)"
Добавлено: 11 апр 2008, 18:08
dimitr
вместо таблицы С в планах почему-то F, кол-во записей в таблицах не указано. Аффтар, что ты от нас хочешь? :-)
Добавлено: 12 апр 2008, 16:29
CyberMax
Прощу прощения. Заменил в запросе имена таблиц и альясов на другие, а планы не переименовал.
Изменил пример на через 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))
То есть, при использовании в подзапросе условия по параметру, применяется более селективный индекс по коду абонента (что правильно), а при использовании условия по полю мастер-таблицы - сначала индекс по коррелируемому поля, а затем джойн и отбор по абоненту.
Добавлено: 13 апр 2008, 18:57
dimitr
если на коленке прикинуть стоимость обоих вариантов, то второй должен быть на порядок дешевле. Соотв-но, я вижу только одно объяснение: зависимость от внешней таблицы заставляет выставить "B" первым потоком джойна независимо от стоимости. Насколько я помню, такая эвристика есть в оптимизаторе, но она рассчитана на порядок потоков внутри одного джойна, а тут косвенно влияет таблица из совсем другого контекста. Непорядок. При возможности попробую разобраться.
Добавлено: 14 апр 2008, 12:41
WildSery
Так это ж совершенно разные запросы.
Во втором запросе вложенный один раз выполняется, вроде бы?
Я честно говоря не понял, как тут в коррелирующем случае можно по-другому план построить.
Разве можно выполнить подзапрос, а уже потом присоединять его к внешнему?
Добавлено: 14 апр 2008, 13:17
dimitr
даже в коррелирующем случае возможны два варианта джойна с разным порядком потоков
Добавлено: 14 апр 2008, 13:28
Merlin
И задача оптимизатора - выбрать наихудший

Прости, наболело
