问题描述
以下查询非常慢。似乎对表中的每一行都执行了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条最新记录以外的所有内容。
所需时间似乎仅取决于表中的记录数,而不取决于要删除的记录数。即使只有10,000条记录,也要花费几分钟。但是,如果我仅执行子选择,则速度很快。
当然在ID
上有一个PK,在SOURCE
上有一个FK和索引(都是整数列)。
解决方法
Firebird 3在DELETE
子句中添加了MERGE
选项。在Release Notes中提到过。另外,pages 16-17的俄语概述中还提供了使用示例。
通过该示例建模,清理查询如下所示:
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)
过滤似乎是多余的,但是我仍然决定添加它,以使查询对于后继读者来说像possibe一样通用。安全胜过遗憾:-)
Firebird 2.x SQL语言手册显示了一个带有MERGE/DELETE
的示例,但这可能是一个文档错误-FB 2.5.9 RelNotes不显示该功能被反向移植。
无论如何,由于缺少MERGE / DELETE和Windows Functions功能,因此可以回到明确的命令式编程并编写良好的旧循环。将需要编写并执行一个小的PSQL程序(永久性的命名存储过程或临时EXECUTE BLOCK
语句),并在SOURCE
值上进行显式循环。
类似的东西(我没有对它进行语法检查,只是从内存中刮擦):
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