如何使用 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
以下查询速度极慢。似乎对 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
.
所需时间似乎仅取决于 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