Я пытаюсь создать запрос, который объединяет внутреннее представление (которое существует для повторного использования), но, очевидно, тот факт, что это представление имеет внутреннее левое соединение, каким-то образом портит оптимизатор, и я не могу понять, почему (статистика индексов обновляется).
Ниже приводится 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
.