如何有效地删除Postgresql 8.1表中的行?

时间:2009-04-22 15:38:23

标签: sql database postgresql

我正在研究PostgreSQL 8.1 SQL脚本,该脚本需要从表中删除大量行。

假设我需要删除的表是Employees(~260K行)。 它有一个名为id的主键。

我需要从此表中删除的行存储在一个名为EmployeesToDelete(~10K记录)的单独临时表中,其中包含一个名为employee_id的Employees.id的外键引用。

有一种有效的方法吗?

起初,我想到了以下内容:

DELETE
FROM    Employees
WHERE   id IN
        (
        SELECT  employee_id
        FROM    EmployeesToDelete
        )

但我听说使用“IN”子句和子查询可能效率低下,尤其是对于较大的表。

我查看了PostgreSQL 8.1文档,并提到了 DELETE FROM ... USING但它没有例子所以我不确定如何使用它。

我想知道以下是否有效并且效率更高?

DELETE
FROM    Employees
USING   Employees e
INNER JOIN
        EmployeesToDelete ed
ON      e.id = ed.employee_id

非常感谢您的评论。

编辑: 我运行了EXPLAIN ANALYZE,奇怪的是第一个DELETE跑得很快(几秒钟内),而第二个DELETE花了这么长时间(超过20分钟)我最终取消了它。

向临时表添加索引有助于提高性能。

以下是对任何感兴趣的人的第一个DELETE的查询计划:

 Hash Join  (cost=184.64..7854.69 rows=256482 width=6) (actual time=54.089..660.788 rows=27295 loops=1)
   Hash Cond: ("outer".id = "inner".employee_id)
   ->  Seq Scan on Employees  (cost=0.00..3822.82 rows=256482 width=10) (actual time=15.218..351.978 rows=256482 loops=1)
   ->  Hash  (cost=184.14..184.14 rows=200 width=4) (actual time=38.807..38.807 rows=10731 loops=1)
         ->  HashAggregate  (cost=182.14..184.14 rows=200 width=4) (actual time=19.801..28.773 rows=10731 loops=1)
               ->  Seq Scan on EmployeesToDelete  (cost=0.00..155.31 rows=10731 width=4) (actual time=0.005..9.062 rows=10731 loops=1)

 Total runtime: 935.316 ms
(7 rows)

此时,除非我能找到更好的写作方式,否则我会坚持使用第一个DELETE。

4 个答案:

答案 0 :(得分:10)

不要猜测,测量。尝试各种方法,看看哪个方法最短。另外,使用EXPLAIN来了解PostgreSQL将要做什么,并查看可以优化的位置。极少数PostgreSQL用户能够猜测正确最快的查询...

答案 1 :(得分:7)

  

我想知道以下是否有效并且效率更高?

    DELETE
    FROM    Employees e
    USING   EmployeesToDelete ed
    WHERE   id = ed.employee_id;

这完全取决于您的索引选择性。

PostgreSQL倾向于为MERGE IN JOIN谓词使用IN,这些谓词具有稳定的执行时间。

只要您已有一个有序的结果集,它就不受满足此条件的行数的影响。

有序的结果集需要排序操作或索引。与PostgreSQL相比,SEQ SCAN中的完整索引遍历效率非常低。

另一方面,JOIN谓词如果您的索引具有非常高的选择性,可能会因使用NESTED LOOPS而受益,而使用HASH JOIN它是不敏感的。

PostgreSQL应该通过估算行数来选择正确的行。

由于您对30k行有260K行,我希望HASH JOIN更有效率,您应该尝试在DELETE ... USING查询上构建计划。< / p>

为了确保,请发布两个查询的执行计划。

答案 2 :(得分:2)

我不确定DELETE FROM ... USING syntax,但一般来说,子查询在逻辑上应该与INNER JOIN相同。数据库查询优化器应该能够(这只是猜测)为两者执行相同的查询计划。

答案 3 :(得分:0)

为什么不能首先删除行而不是将它们添加到EmployeesToDelete表中?

或者,如果您需要撤消,只需向Employees添加一个“已删除”标记,这样您就可以在一个表中反转删除或永久删除?