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)
.
总的来说,这可能只会更慢,因为单行“精确定位” - 但至少它会分散延迟。
但正如我在上一个问题的回答中所说,最好花时间调查减速的根源,而不是通过删除数据来解决它。
如果您需要删除的数据在存储过程启动后永远不需要回滚,还有另一种方法可以处理存储过程中的大量 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
我需要删除一堆记录(实际上是数百万条),但由于性能问题,我不想在单独的语句中进行删除。所以我创建了一个视图:
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)
.
总的来说,这可能只会更慢,因为单行“精确定位” - 但至少它会分散延迟。
但正如我在上一个问题的回答中所说,最好花时间调查减速的根源,而不是通过删除数据来解决它。
如果您需要删除的数据在存储过程启动后永远不需要回滚,还有另一种方法可以处理存储过程中的大量 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