【问题标题】:How to "merge" two tables efficiently in SQL?如何在 SQL 中有效地“合并”两个表?
【发布时间】:2014-09-05 09:47:27
【问题描述】:

我有两个结构相同的表(例如“foo”和“bar”)和一个主键(例如“a”):

CREATE TABLE foo(a INT PRIMARY KEY, b VARCHAR(10));
INSERT INTO foo(a, b) VALUES (1, 'foo'), (2, 'foo');
CREATE TABLE bar(a INT PRIMARY KEY, b VARCHAR(10));
INSERT INTO bar(a, b) VALUES (2, 'bar'), (3, 'bar');

现在我想用主键匹配的表'bar'中的行的值更新表'foo'的行,并且我想将表'bar'中的行插入表'如果主键在表 'foo' 中不存在,则 foo':

UPDATE foo SET b = bar.b FROM bar WHERE foo.a = bar.a;
INSERT INTO foo SELECT bar.* FROM bar LEFT JOIN foo USING (a) WHERE foo.a IS NULL;

这是我想要的,但我想知道是否有更有效的方法来做到这一点?

【问题讨论】:

  • 也许使用 MERGE 语句?
  • PostgreSQL 似乎没有 MERGE 语句。
  • 是的;我刚刚从documentation 中找到了。它明确列出了 PostgreSQL 9.3.5 不支持的 F312 MERGE 语句(以及 F313 和 F314,MERGE 的装饰)。在没有 MERGE 的情况下,我怀疑是否有比您已经在做的更好的方法来做这件事,被事务或保存点包围。
  • 搜索“Postgresql upsert”
  • 你是怎么遇到这种情况的?您可能想查看两个表不同步的原因/原因

标签: sql postgresql postgresql-9.3


【解决方案1】:

如果其他人(尚未)同时访问您的表,您可以使用 FULL [OUTER] JOIN 查询创建一个新的合并表,其中 bar 中的值优先。

如果您有并发访问权限,但有能力锁定两个表,那也可以:

BEGIN;
LOCK foo, bar;                     -- if you need it

CREATE TABLE baz AS
SELECT a, COALESCE(b.b, a.b) AS b  -- bar gets priority
FROM   foo f
FULL   JOIN bar b USING (a)
ORDER  BY a;                       -- optional order by

-- you need table name foo?
DROP  TABLE foo, bar;
ALTER TABLE baz RENAME TO foo;
ALTER TABLE foo ADD CONSTRAINT foo_a_pkey PRIMARY KEY (a);
-- do more?

COMMIT;

如果您有大量重叠,那么编写一个没有死行的新的原始(集群)表比更新大部分旧表更有效。如果重叠不大,更新/插入可能更有效。如果两个表都很小,请不要费心并采用简单的解决方案。

新表显然没有旧表的任何索引或约束。重新创建你需要的东西。

如果您有很多依赖对象(视图、函数),您可能希望保留旧表。而是创建一个临时表 TRUNCATE foo 并将数据写回到同一个表中。这也不会终止等待现有表的并发事务。

BEGIN;
LOCK foo, bar;                     -- if you need it

SET temp_buffers = 500MB;          -- if you need it

CREATE TEMP TABLE tmp AS
SELECT a, COALESCE(b.b, a.b) AS b  -- bar gets priority
FROM   foo f
FULL   JOIN bar b USING (a);

-- for performance, you might want to drop indexes and constraints here ...
TRUNCATE foo;
INSERT INTO foo
SELECT * FROM tmp
ORDER BY a;                         -- optional

DROP  TABLE  bar;                   -- optional
-- ... and recreate previously dropped indexes and constraints here

COMMIT;

关于temp_buffers

并发写入会发生什么?

【讨论】:

  • 感谢您提供全面的答案,但是与我已经使用 INSERT 和 UPDATE 语句所做的相比,这如何更有效或更简单或以任何其他方式更好?
  • @ChristianSchlichtherle:结果是相同的,逻辑上(必须如此)。但是性能优越(取决于所描述的情况)并且您得到一个原始(可选“集群”)表,索引和表中没有死行。特别是对于大表和/或表上的大量约束/索引。
猜你喜欢
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 1970-01-01
  • 2011-11-18
  • 2015-05-29
  • 1970-01-01
  • 2014-08-17
相关资源
最近更新 更多