【问题标题】:Firebird truncate table / delete all rowsFirebird 截断表/删除所有行
【发布时间】:2012-12-11 10:23:38
【问题描述】:

我使用的是 Firebird 2.5.1 Embedded。我已经按照惯例清空了将近 200k 行的表:

delete from SZAFKI

这是输出,看它需要 16 秒,这是不可接受的。

Preparing query: delete from SZAFKI
Prepare time: 0.010s
PLAN (SZAFKI NATURAL)

Executing...
Done.
3973416 fetches, 1030917 marks, 116515 reads, 116434 writes.
0 inserts, 0 updates, 182658 deletes, 27 index, 182658 seq.
Delta memory: -19688 bytes.
SZAFKI: 182658 deletes. 
182658 rows affected directly.
Total execution time: 16.729s
Script execution finished.

Firebird 没有 TRUNCATE 关键字。由于查询使用 PLAN NATURAL,我尝试手动计划查询,如下所示:

delete from szafki PLAN (SZAFKI INDEX (SZAFKI_PK))

但是 Firebird 说“SZAFKI_PK 不能在指定的计划中使用”(它是一个主键) 问题是我如何有效地清空表格?无法删除和重新创建。

【问题讨论】:

  • “无法删除和重新创建。”为什么不呢?
  • 您是否有针对指定表的 ON DELETE 触发器?
  • 删除约束(PK 和 FK)然后删除数据,然后重新创建约束。
  • 计划在没有WHERE条件的情况下对删除没有用处:无论如何它都需要扫描整个表,并且由于Firebird中数据的方向,遵循索引顺序甚至会受到伤害,因为它将随机遍历表的所有页面,而不是按存储顺序扫描表的页面。
  • 要尝试的另一件事是DELETE FROM SZAFKI WHERE ID > 0(假设 id 为 1 或更高)。顺便说一句,这是一张在最终删除之前更新、删除和插入行的表吗?当我在具有 200K 记录的表上删除时(新填充,因此没有垃圾,也没有数据页碎片),它在 1.5 秒内完成。

标签: sql firebird sql-delete truncate


【解决方案1】:

根据我的评论回答

您可以尝试的一个技巧是使用DELETE FROM SZAFKI WHERE ID > 0(假设ID 为1 或更高)。这将强制 Firebird 使用主键索引查找行。

我最初的假设是这会比未索引的删除更糟糕。未索引的删除将对表的所有数据页进行顺序扫描并删除行(即:创建一个新的记录版本,它是已删除的存根记录)。当您使用索引时,它将按索引顺序查找行,这将导致随机遍历数据页(假设由于插入、删除和更新导致的大量记录版本导致数据中的高度碎片化)。我曾预计这会更慢,但可能会导致 Firebird 只需要读取相关数据页(具有与事务相关的记录版本)而不是表的所有数据页。

【讨论】:

  • 我不知道任何 Firebird 的内部结构以及它如何处理数据,虽然我应该,可能,但无论如何这都是可行的。正如 WarmBooter 所建议的那样,在我的情况下,提交不需要任何长时间,所以我会接受这个意想不到的解决方案。
  • 提交本身不会花费很长时间,但是表上的下一次选择可能会触发垃圾收集(实际上是删除不再对任何事务感兴趣的旧记录版本),这可能是性能下降。
【解决方案2】:

不幸的是,目前的 Firebird 版本没有快速的方法对整个(大)表进行大规模删除。当“删除的内容”被垃圾收集时,您可能会期待更高的延迟(在提交删除后在表中运行 select *,您会看到)。您可以在删除之前尝试停用该表中的索引,看看是否有帮助。 如果您将表用作某种临时存储,我建议您使用 GTT 功能。

【讨论】:

  • 没有临时表,只是清空其中的许多表并在几周内再次填满它们。叮,这在我的 i7 上需要很长时间,所以在可能旧的笔记本电脑上使用我的应用程序的人一定很沮丧。我正在尝试通过操作、测试等来减少操作时间,这样我就不必处理他们的意外故障,而且,嗯......看起来不错:D
【解决方案3】:

最快并且唯一的方法是从 FireBird 表中的 所有数据快速删除 - dropcreate再次表。至少对于目前的官方版本 2.5.X 。 FireBird 3.0 的路线图中没有截断运算符,测试版已经出,所以很可能在 3.0 中也没有截断。

此外,您可以使用运算符 RECREATE - 与创建相同的语法。如果表存在,RECRATE 删除它,然后创建新表。如果表不存在,则 recreate 只是创建它。

 RECREATE TABLE Table1 (
   ID    INTEGER,
   NAME  VARCHAR(20),
   DATE  DATE,
   T     TIME
 );

【讨论】:

  • 有没有办法在没有列描述的情况下重新创建(没有任何更改)?
猜你喜欢
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 2014-05-15
  • 1970-01-01
  • 1970-01-01
  • 2015-01-26
  • 2020-04-11
  • 1970-01-01
相关资源
最近更新 更多