Sql 内部联接已离开联接的视图/子查询时出现错误的计划
我试图构建一个内部连接视图的查询,该视图是为了可重用而存在的,但是很明显,这个视图有一个内部左连接的事实在某种程度上扰乱了优化器,我真的不明白为什么索引统计信息会被更新 下面是一个MCVE。其实很简单。您可以将其想象为一个简单的客户B-订单C设计,其中客户地址可选在另一个表a中。然后,我们可以将客户加入到其地址vw_B中 元数据和示例数据:Sql 内部联接已离开联接的视图/子查询时出现错误的计划,sql,query-optimization,firebird,Sql,Query Optimization,Firebird,我试图构建一个内部连接视图的查询,该视图是为了可重用而存在的,但是很明显,这个视图有一个内部左连接的事实在某种程度上扰乱了优化器,我真的不明白为什么索引统计信息会被更新 下面是一个MCVE。其实很简单。您可以将其想象为一个简单的客户B-订单C设计,其中客户地址可选在另一个表a中。然后,我们可以将客户加入到其地址vw_B中 元数据和示例数据: create table A ( id int not null, fieldA char(10) not null, const
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;
重用vw_B以自动联接表。优化器在VW_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。
< P>火鸟优化器不够智能,无法考虑查询等价。 您对view的查询相当于: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在评论中指出的那样,没有通用的解决方案。这些查询并不完全相同,它更像是从C内部连接中缩短的select*从B左连接A.id=B.idA x on x.id=C.idB,这也改变了优化器处理它的方式。顺便说一句:你用的是哪个火鸟版本?@markrotterveel-ops,对不起。编辑问题以添加版本:WI-V3.0.4.33054。是的,我想它更像一个子查询。事实上,子查询也有同样的错误计划问题。但我不明白为什么子查询/视图版本不能优化到与直接连接版本相同的计划。问题之一是Firebird优化器没有那么好。@MarkRotterVeel优化器没有那么好这是此问题的正确答案:。更具体地说:外部连接在firebird中处理得不是很好一个很好的例子:。根据我的经验,视图中的外部联接总是单独计算的,因此优化器无法更改OP中内部联接的顺序。如果视图中没有外部联接,则两个查询应具有相同的计划。是否有通用解决方案?我不这么认为。如果连接类型重要,可以通过将内部连接更改为左外部连接并添加NOTNULL谓词来提高Q中的性能。当连接表中只需要一个字段时,我还经常在视图中使用子查询而不是左连接。
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'