Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 内部联接已离开联接的视图/子查询时出现错误的计划_Sql_Query Optimization_Firebird - Fatal编程技术网

Sql 内部联接已离开联接的视图/子查询时出现错误的计划

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

我试图构建一个内部连接视图的查询,该视图是为了可重用而存在的,但是很明显,这个视图有一个内部左连接的事实在某种程度上扰乱了优化器,我真的不明白为什么索引统计信息会被更新

下面是一个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;
重用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'