【问题标题】:Copying Rows of Table0 to Table2 Where Same Rows Do Not Exist in Table1 (PostgreSQL)将表 0 的行复制到表 1 中不存在相同行的表 2 (PostgreSQL)
【发布时间】:2012-10-03 03:00:16
【问题描述】:

谁能告诉我以下哪一项更有效?我有数千万行要处理,性能至关重要。

在第二个示例中,table0 是一个临时表,它的创建似乎比第一个示例中的 table0 快得多。 (为什么?)在第一个示例中我不能使用临时表,因为在创建表之前无法声明变量行。 (table0 保存了原始表的所有不同行,下面的代码中没有显示。)

我想在第二个示例中为table1 中的blah2blah3blah4blah5 创建哈希索引是个好主意,尽管这样会花费更长的时间来写入桌子。


FOR row IN SELECT * FROM table0
LOOP
  IF NOT EXISTS (SELECT 1 FROM table1
                   WHERE blah2 = row.blah2 AND blah3 = row.blah3
                     AND blah4 = row.blah4 AND blah5 = row.blah5) THEN
    INSERT INTO table2
      (blah0, blah1, blah2, blah3, blah4, blah5)
      VALUES (row.blah0, row.blah1, row.blah2, row.blah3, row.blah4, row.blah5);
  END IF;
END LOOP;

INSERT INTO table2
  (blah0, blah1, blah2, blah3, blah4, blah5)
  SELECT blah0, blah1, blah2, blah3, blah4, blah5 FROM table0
    WHERE NOT EXISTS
      (SELECT 1 FROM table1
         WHERE table1.blah2 = table0.blah2
           AND table1.blah3 = table0.blah3
           AND table1.blah4 = table0.blah4
           AND talbe1.blah5 = table0.blah5);

【问题讨论】:

  • 很难准确说出您的要求。您似乎在谈论以不同方式创建的两个不同的table0s(如何?为什么?)。另外,不要使用哈希索引,与 b-tree 相比几乎没有优势,而且它们维护得不好,不支持复制等。
  • 如果比较性能使用EXPLAIN ANALYZE。看看这两个,我预计第二个比第一个大大快,但你应该在缩小的样本上测量它,而不仅仅是猜测。
  • 我觉得问题已经很清楚了。

标签: sql performance postgresql plpgsql temp-tables


【解决方案1】:

问题

在第二个例子中,table0 是一个临时表,它似乎是 创建比第一个示例中的 table0 快得多。 (为什么?)

临时表通常比普通表快很多,因为它们的内容不会持久化到磁盘。当您创建一个时,您仍然有少量磁盘活动,因为系统目录会收到条目。

当您用完临时缓冲区时,临时表的性能就会下降,因为系统开始将页面换出到磁盘。您可以在会话开始时设置 temp_buffers 为临时表提供更多 RAM。更多详情请见this related answer

如果最后你想把结果保存在某个地方,你不妨在第二个例子中立即使用常规表。

第一个示例也非常慢,因为循环单个行通常比使用 SQL 命令的基于集合的操作更昂贵很多

我不能在第一个示例中使用临时表,因为 在创建表之前无法声明变量行。 (table0 包含原始表的所有不同行,这不是 如下代码所示。)

好吧,在创建表之前,您不能在函数中使用行类型。但是您可以只使用匿名记录:

DECLARE
   rec record;
BEGIN
   FOR rec IN SELECT * FROM table0 ...

替代解决方案

但是你的第一种方法无论如何都不好。你的第二种方法看起来很好。应该是最快的方法。另一种选择是:

INSERT INTO table2 (blah0, blah1, blah2, blah3, blah4, blah5)
SELECT t0.blah0, t0.blah1, t0.blah2, t0.blah3, t0.blah4, t0.blah5
FROM   table0 t0
LEFT   JOIN table1 t1 USING (blah2, blah3, blah4, blah5)
WHERE  t1.t1_id IS NULL -- or any other column defined NOT NULL

如果您在table1 中有重复的(blah2, blah3, blah4, blah5) 条目,那么您使用EXISTS 的查询可能会更快。否则,LEFT JOIN / IS NULL 可能会名列前茅。

索引和测试

当然,table1 中的(blah2, blah3, blah4, blah5) 上的索引会有很大帮助 - 增加了在 table1 中写入操作的一些成本。让它成为一个普通的 b-tree 索引(就像 @Craig 在评论中已经建议的那样),但我会选择 multi-column index。这种情况下更便宜、更快!

按照@Craig 在评论中的建议使用EXPLAIN ANALYZE 来测试性能 - 或者首先使用EXPLAIN(没有ANALYZE),因为您的表格似乎很大,而EXPLAIN ANALYZE 伪造执行所有这些以变得真实次。

【讨论】:

  • 非常感谢欧文和克雷格。我试图使问题尽可能简洁,但是,作为一个新成员,我不确定如何很好地格式化它们。这个答案非常全面,正是我希望找到的!再次感谢您的宝贵时间!
  • 顺便说一句,因为我只检查等式(而不是不等式),如果我忽略维护问题,使用哈希索引会比使用 b-tree 索引更有效吗?我不确定这是否是个好主意,但在搜索具有匹配值的行时,性能似乎存在一些差异。
  • 而且我在 table1 中没有 (blah2, blah3, blah4, blah5) 的重复条目,所以上面的替代解决方案可能是最快的,我想我应该使用 EXPLAIN ANALYZE 检查。
  • @user1740028:只要您不处理大列,b-tree 索引应该就可以了。通过检查 EXPLAIN 确保使用了索引。当然,所有关于性能优化的基本建议都适用。检查Postgres Wiki page。现在有点过时了,但所有基础知识仍然适用。
  • 这是一组非常有用的链接。谢谢!
猜你喜欢
  • 2016-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多