如何使用 Firebird 的子选择优化 SQL 删除查询?

How to optimize SQL delete query with subselect for Firebird?

以下查询速度极慢。似乎对 table 中的每一行都执行了 subselect?!

delete from HISTORY
where ID in (
  select ID from (
    select ID, ROW_NUMBER() over(partition by SOURCE order by ID desc) as NUM from HISTORY
  ) where NUM > 100
);

这是一个清理查询。它应该删除除每个 SOURCE.

的 100 条最新记录之外的所有内容

所需时间似乎仅取决于 table 中的记录数,而不取决于要删除的记录数。即使只有 10,000 条记录,也需要几分钟时间。但是,如果我只执行sub-select,它很快。

当然ID上有主键,SOURCE上有外键和索引(都是整数列)。

Firebird 3 在 MERGE 子句中添加了 DELETE 选项。它在 Release Notes. It is now properly documented in Firebird 3 SQL Reference.

中首次被提及

通过那里的示例建模,清理查询看起来像这样:

merge into HISTORY HDel
using ( select ID, SOURCE, ROW_NUMBER() over
                (partition by SOURCE order by ID desc) as NUM 
        from HISTORY ) HVal
   on (HVal.NUM > 100) and (HVal.ID = HDel.ID) and (HVal.Source = HDel.Source)
WHEN MATCHED THEN DELETE

在您的特定数据库中 (HVal.Source = HDel.Source) 过滤似乎是多余的,但我仍然决定添加它以使查询尽可能通用,以供将来的读者使用。安全总比后悔好:-)


Firebird 2.x 没有提供该功能,而 FB3 的 MERGE/DELETE 和 Window 功能缺少一个可以回退到显式命令式编程并编写良好的旧循环。这将需要编写和执行一个小的 PSQL 程序(一个持久的命名存储过程或临时 EXECUTE BLOCK 语句)并对 SOURCE 值进行显式循环。

类似的东西(我没有syntax-check,只是凭记忆):

execute block as
declare variable SRC_VAL integer;
declare variable ID_VAL integer;
begin
  for select distinct SOURCE from HISTORY into :SRC_VAL do begin
     :ID_VAL = NULL;
     select first(1) skip(100) ID from HISTORY
       where SOURCE = :SRC_VAL
       order by ID desc
       into :ID_VAL;
     if (:ID_VAL IS NOT NULL) then
       delete from HISTORY 
         where SOURCE = :SRC_VAL 
           and ID <= :ID_VAL;
  end
end