从Postgres批量删除行的最有效方法


23

我想知道最有效的方法是从PostgreSQL删除大量行,这一过程每天都会是将数据批量导入(插入和删除的增量)到表中的一项重复任务。可能要删除数千行,甚至数百万行。

我有一个主键文件,每行一个。我所考虑的两个选项与以下内容类似,但我对PostgreSQL的内部知识了解不足/不了解,无法做出明智的决定,这是最佳选择。

  • DELETE使用简单WHERE的主键对文件中的每一行执行查询(或n使用IN()子句分批对删除进行分组)
  • 使用COPY命令将主键导入到临时表中,然后使用连接从主表中删除

任何建议将不胜感激!


1
此处已更详细地回答了相同的问题:stackoverflow.com/a/8290958
Simon

Answers:


25

您的第二个选择要干净得多,并且性能会足以使其值得。您的替代方法是建立庞大的查询,这对计划和执行将是一个很大的麻烦。总的来说,让PostgreSQL在这里工作会更好。总的来说,我发现以您描述的适当方式对数万行进行了更新,但是要避免这样做是一件重要的事情。

做到这一点的方法是在删除中使用选择和联接。

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

在任何情况下,您都不应使用大型表:

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

这通常会导致嵌套循环反连接,从而使性能产生问题。如果最终不得不走那条路线,请执行以下操作:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL通常非常擅长避免错误的计划,但是仍然有涉及外部联接的案例,这可能在好的和错误的计划之间产生很大的不同。

这在更远的地方徘徊,但是我认为值得一提,因为从IN到NOT IN并观察查询性能槽是多么容易。


这很有帮助,谢谢!但是,我发现在这种特殊情况下,使用“组合查询”更为有效。例如, IN ( select id from foo except select id from rows_to_keep ) 请参阅postgresql.org/docs/9.4/static/queries-union.html
Ufos

1

我遇到了这个问题,因为我遇到了类似的问题。我正在清理具有300M +行的数据库,最终数据库将仅包含原始数据的30%。如果您遇到类似的情况,则实际上更容易插入新表并重新索引而不是删除。

做类似的事情

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

通过对foo和bar进行适当的索引编制,可以避免进行Seq扫描。

然后,您将不得不重新索引并重命名表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.