Firebird 中批量删除的存储过程

Stored Procedure for batch delete in 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;

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

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

ERROR: Dynamic SQL Error; SQL error code = -104; Token unknown; WHILE

代码:

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

我实在想不通。

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

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

这就是我想尝试批量删除的原因。那里的减速问题纯粹是硬件问题——硬盘驱动器不见了,我们更换了它。之后执行语句和做备份还原回收磁盘都没有问题space.

because of performance issues

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

例如,Interbase/Firebird引擎中的Select Count(*)意味着对所有table的自然扫描,垃圾收集经常被它触发,如果创建了大量垃圾,它本身就会变长(无论是通过一百万行语句还是百万行语句完成,大量删除肯定会这样做)。

如果你真的想减慢删除速度 - 你必须全天候传播 activity,并让你的客户端应用程序调用删除 SP,例如每 15 分钟一次。您必须向 table 添加一些列,标记它已标记为删除,然后像那样做

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).

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

使用删除...行。
https://firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-dml-delete-orderby

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

如果您需要删除的数据在存储过程启动后永远不需要回滚,还有另一种方法可以处理存储过程中的大量 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