Firebird 中批量删除的存储过程

问题描述

我需要删除一堆记录(字面意思是数百万),但由于性能问题,我不想在单个语句中进行删除。所以我创建了一个视图:

CREATE VIEW V1 
AS 
    SELECT FirsT 500000 * 
    FROM TABLE 
    WHERE W_ID = 14

之后我做了一堆删除,例如:

DELETE FROM V1 WHERE TS < 2021-01-01 

我想要的是在 While loop 和存储过程中导入此逻辑。我试过这样的 SELECT COUNT 查询

SELECT COUNT(*) 
FROM TABLE 
WHERE W_ID = 14 AND TS < 2021-01-01;

我可以在与条件相同的过程中使用这个数字吗?我该如何管理?

这是我尝试过的,但出现错误

错误:动态 sql 错误sql 错误代码 = -104;令牌未知;当

代码

CREATE PROCEDURE DeleteBatch
  AS
  DECLARE VARIABLE CNT INT;
  BEGIN
       SELECT COUNT(*) FROM TABLE WHERE W_ID = 14 AND TS < 2021-01-01 INTO :cnt;
        WHILE cnt > 0 do
         BEGIN
          IF (cnt > 0) THEN
          DELETE FROM V1 WHERE TS < 2021-01-01;
         END
     ELSE break;
  END

我就是无法理解这个。

澄清一下,在我的 previous question 中,我想知道如何在删除许多记录后管理 garbage_collection,我做了建议 - SELECT * FROM TABLE;gfix -sweep效果很好。 正如评论中提到的,正确的说法是 SELECT COUNT(*) FROM TABLE;

在那之后,另一个更大的数据库给了我 - 超过 5000 万。问题是数据库的运行速度非常慢。我设法找到了它所在的服务器,用 DELETE 语句杀死了它以清理数据库

这就是我想尝试批量删除的原因。那里的减速问题纯粹是硬件 - HDD 不见了,我们更换了它。之后,执行语句和进行备份和恢复以回收磁盘空间都没有问题。

解决方法

假设您需要删除的数据,一旦存储过程启动就不需要回滚,还有另一种方法可以处理存储过程中的大量 DELETE。

示例存储过程将一次删除 500,000 行。它将循环直到没有更多行要删除。 AUTONOMOUS TRANSACTION 将允许您将每个删除语句放在其自己的事务中,并在语句完成后立即提交。这是在存储过程中发出隐式提交,而您通常无法这样做。

CREATE OR ALTER PROCEDURE DELETE_TABLEXYZ_ROWS
AS
DECLARE VARIABLE RC INTEGER;
BEGIN

  RC = 9999;

  WHILE (RC > 0) DO
  BEGIN

    IN AUTONOMOUS TRANSACTION DO
    BEGIN
      DELETE FROM TABLEXYZ ROWS 500000;

      RC = ROW_COUNT;
    END
  END
  SELECT COUNT(*)
  FROM TABLEXYZ
  INTO :RC;
END
,

因为性能问题

那些到底是什么?我不认为你实际上是在提高性能,只是在循环中运行 delete 但在同一事务中,或者甚至在相同的时间跨度内运行不同的 TX。你似乎在解决一些错误的问题。问题不在于您如何创建“垃圾”,而在于 Firebird 如何以及何时“收集”它。

例如,Interbase/Firebird 引擎中的 Select Count(*) 表示对所有表进行自然扫描,并且垃圾收集通常由它触发,如果大量垃圾被收集,它本身会变长创建(并且大规模删除肯定会,无论是由一百万行语句还是百万行语句完成)。

How to delete large data from Firebird SQL database

如果您真的想减慢删除速度 - 您必须全天候传播该活动,并使您的客户端应用程序例如每 15 分钟调用一次删除 SP。您将不得不向表中添加一些列,将其标记为删除,然后执行这样的工作

CREATE PROCEDURE DeleteBatch(CNT INT)
AS
DECLARE ROW_ID INTEGER;
BEGIN
  FOR SELECT ID FROM TABLENAME WHERE MARKED_TO_DEL > 0 INTO :row_id
  DO BEGIN
     CNT = CNT - 1;
     DELETE FROM TABLENAME WHERE ID = :ROW_ID;
     IF (CNT <= 0) THEN LEAVE;  
  END
  SELECT COUNT(1) FROM TABLENAME INTO :ROW_id; /* force GC now */
END

...并且每 15 分钟你做一次 EXECUTE PROCEDURE DeleteBatch(1000)

总的来说,这可能只会更慢,因为单行“精确定位” - 但至少它会分散延迟。

,

使用 DELETE...ROWS。
How can I fork my own GitHub repository?

但正如我在上一个问题的回答中已经说过的那样,最好花时间调查减速的根源,而不是通过删除数据来解决它。