Неправильный план при внутреннем объединении представления / подзапроса, который оставил соединение - PullRequest
1 голос
/ 24 апреля 2020

Я пытаюсь создать запрос, который объединяет внутреннее представление (которое существует для повторного использования), но, очевидно, тот факт, что это представление имеет внутреннее левое соединение, каким-то образом портит оптимизатор, и я не могу понять, почему (статистика индексов обновляется).

Ниже приводится MCVE. Это на самом деле очень просто. Вы можете представить его как простой дизайн клиента (B) - заказа (C), в котором адрес клиента (необязательно) находится в другой таблице (A). И затем у нас есть возможность присоединить клиента к его адресу (vw_B).

Метаданные и примеры данных:

create table A (
    id int not null,
    fieldA char(10) not null,

    constraint pk_A primary key (id)
);

create table B (
    id int not null,
    fieldB char(10) not null,
    idA int,

    constraint pk_B primary key (id),
    constraint fk_A foreign key (idA) references A (id)
);

create view VW_B as
    select b.*, a.fieldA from B
    left join A on a.id = b.idA;

create table C (
    id int not null,
    mydate date not null,
    idB int not null,

    constraint pk_C primary key (id),
    constraint fk_B foreign key (idB) references B (id)
);
create index ix_C on C (mydate);

insert into A (id, fieldA)
with recursive n as (
    select 1 as n from rdb$database
    union all
    select n.n + 1 from n
    where n < 10
)
select n.n, 'A' from n;
SET STATISTICS INDEX PK_A;

insert into B (id, fieldB, idA)
with recursive n as (
    select 1 as n from rdb$database
    union all
    select n.n + 1 from n
    where n < 100
)
select n.n, 'B', IIF(MOD(n.n, 5) = 0, null, MOD(n.n, 10)+1) from n;
SET STATISTICS INDEX PK_B;
SET STATISTICS INDEX FK_A;

insert into C (id, mydate, idB)
with recursive n as (
    select 1 as n from rdb$database
    union all
    select n.n + 1 from n
    where n < 1000
)
select n.n, cast('01.01.2020' as date) + 100*rand(), mod(n.n, 100)+1 from n;
SET STATISTICS INDEX PK_C;
SET STATISTICS INDEX FK_B;
SET STATISTICS INDEX IX_C;

С этим дизайном я хочу иметь запрос, который может присоединиться все таблицы таким образом, чтобы я мог эффективно выполнять поиск заказов по дате (c .mydate) или по любой индексированной информации о клиенте (таблица B). Очевидный выбор - это внутреннее соединение между B и C, и оно отлично работает. Но если я хочу добавить адрес клиента к результату, используя vw_B вместо B, оптимизатор больше не выбирает лучший план.

Вот несколько запросов, чтобы показать это:

Присоединение вручную все таблицы и фильтрация по дате. Оптимизатор работает нормально.

select c.*, b.fieldB, a.fieldA from C
inner join B on b.id = c.idB
left join A on a.id = b.idA
where c.mydate = '01.01.2020'

PLAN JOIN (JOIN (C INDEX (IX_C), B INDEX (PK_B)), A INDEX (PK_A))

Повторное использование vw_B для автоматического присоединения таблицы. Оптимизатор выбирает ЕСТЕСТВЕННЫЙ план на (VW_B B).

select c.*, b.fieldB, b.fieldA from C
inner join VW_B b on b.id = c.idB
where c.mydate = '01.01.2020'

PLAN JOIN (JOIN (B B NATURAL, B A INDEX (PK_A)), C INDEX (FK_B, IX_C))

Почему это происходит? Я думал, что эти два запроса должны произвести точно такую ​​же операцию в двигателе. Теперь это очень простой MVCE, и у меня гораздо более сложные представления, которые можно многократно использовать, и объединение больших таблиц с этими представлениями вызывает проблемы с производительностью.

Есть ли у вас какие-либо предложения по улучшению производительности / ПЛАН выбор, но сохраняя удобство повторного использования, предоставляемое представлениями?

Версия сервера WI-V3.0.4.33054.

1 Ответ

1 голос
/ 26 апреля 2020

Оптимизатор Firebird недостаточно интеллектуален, чтобы считать запросы эквивалентными.

Ваш запрос с представлением эквивалентен:

select c.*, b.fieldB, a.fieldA from C
inner join (B left join A on a.id = b.idA)
on b.id = c.idB
where c.mydate = '01.01.2020'

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

Как указывает BrakNicku в комментариях, для этого нет общего решения.

...