使用 in (select ...) 子句进行查询优化

Query optimization with in (select ...) clause

我在 Windows 上使用 Firebird WI-V3.0.4.33054。

我在优化此查询时遇到问题,它使用了带有 select:

的 in 子句
update CADPC p set p.STA = 'L'
where p.COD in (select distinct CODPC from CADPCI_Rec where IDNfr = 27)
and not exists (select * from CADPCI where CODPC = p.COD)

此查询的计划是(明显的问题是 P NATURAL 部分):

PLAN SORT (CADPCI_REC INDEX (PK_CADPCI_REC))
PLAN (CADPCI INDEX (FK_CADPCI_CODPC))
PLAN (P NATURAL)

Select Expression
    -> Filter
        -> Unique Sort (record length: 36, key length: 8)
            -> Filter
                -> Table "CADPCI_REC" Access By ID
                    -> Bitmap
                        -> Index "PK_CADPCI_REC" Range Scan (partial match: 1/3)
Select Expression
    -> Filter
        -> Table "CADPCI" Access By ID
            -> Bitmap
                -> Index "FK_CADPCI_CODPC" Range Scan (full match)
Select Expression
    -> Filter
        -> Table "CADPC" as "P" Full Scan

另一方面,如果我手动 运行 select distinct,复制结果并粘贴到查询中,如下所示:

update CADPC p set p.STA = 'L'
where p.COD in (5699, 5877, 5985)
and not exists (select * from CADPCI where CODPC = p.COD)

现在优化器非常快速地为 P table 和查询 运行 选择一个合理的计划:

PLAN (CADPCI INDEX (FK_CADPCI_CODPC))
PLAN (P INDEX (PK_CADPC, PK_CADPC, PK_CADPC))

Select Expression
    -> Filter
        -> Table "CADPCI" Access By ID
            -> Bitmap
                -> Index "FK_CADPCI_CODPC" Range Scan (full match)
Select Expression
    -> Filter
        -> Table "CADPC" as "P" Access By ID
            -> Bitmap Or
                -> Bitmap Or
                    -> Bitmap
                        -> Index "PK_CADPC" Unique Scan
                    -> Bitmap
                        -> Index "PK_CADPC" Unique Scan
                -> Bitmap
                    -> Index "PK_CADPC" Unique Scan

我也试过在这两种情况下都存在,但结果是一样的:子查询为每一行重新计算。

update CADPC p set p.STA = 'L'
where exists (select * from CADPCI_Rec where IDNfr = 27 and CODPC = p.COD)
and not exists (select * from CADPCI where CODPC = p.COD)

计划:

PLAN (CADPCI_REC INDEX (PK_CADPCI_REC))
PLAN (CADPCI INDEX (FK_CADPCI_CODPC))
PLAN (P NATURAL)

Select Expression
    -> Filter
        -> Table "CADPCI_REC" Access By ID
            -> Bitmap
                -> Index "PK_CADPCI_REC" Range Scan (partial match: 1/3)
Select Expression
    -> Filter
        -> Table "CADPCI" Access By ID
            -> Bitmap
                -> Index "FK_CADPCI_CODPC" Range Scan (full match)
Select Expression
    -> Filter
        -> Table "CADPC" as "P" Full Scan

所以,问题是:当 in 子句包含 select(通常只有几条记录)时,我能否以某种方式让引擎选择索引计划?

尝试对两者使用 exists

update CADPC p
    set p.STA = 'L'
    where exists (select 1 from CADPCI_Rec where r.IDNfr = 27 and p.COD = r.CODPC) and
         not exists (select 1 from CADPCI c2 where c2.CODPC = p.COD);

特别是,您想要 CADPCI_Rec(CODPC, IDNfr) 上的索引。

您可以尝试 EXECUTE BLOCK 和 "invert control"

本质上是发布一个匿名的临时存储过程

EXECUTE BLOCK AS
  declare id INTEGER;
BEGIN
  for select distinct t1.CODPC from CADPCI_Rec t1 
    left join CADPCI t2 on where t2.CODPC = t1.CODPC
  where t2.CODPC is NULL and t1.IDNfr = 27
  into :id
  do 
    update CADPC p set p.STA = 'L' where p.COD = :ID and p.STA <> 'L';
END

您还可以使用全局临时表 (GTT)

然后在实际删除之前创建 ID 列表。

数据库准备(创建无主体table):

CREATE GLOBAL TEMPORARY TABLE CADPC_mark_IDs
   ( ID integer )
ON COMMIT DELETE ROWS

然后命令就像

insert into CADPC_mark_IDs(ID)
select distinct t1.CODPC from CADPCI_Rec t1 
   left join CADPCI t2 on where t2.CODPC = t1.CODPC
where t2.CODPC is NULL and t1.IDNfr = 27

然后

update CADPC p set p.STA = 'L'
where p.COD in (select * from CADPC_mark_IDs) and p.STA <> 'L'

然后

commit; -- clear the in-memory table for next uses

还有一个选项,就像 Mark 建议的那样,在将 "where not exist" 转换为 "left join" 之后使用 MERGE(上面已经完成,希望正确)。

沿途的事情

merge into CADPC p
  using (
    select distinct t1.CODPC as id from CADPCI_Rec t1 
      left join CADPCI t2 on where t2.CODPC = t1.CODPC
    where t2.CODPC is NULL and t1.IDNfr = 27
  ) t
on (t.id = p.COD) and (p.STA <> 'L')
when matched then update set p.STA = 'L'