0

你可能认为我是 PostgreSQL 初学者,这个问题的目的是深入了解如何从 PostgreSQL 中获得最佳性能来解决这个问题。我有两张表,它们的结构相同,但内容不同。

|Table A|
key - value
1     dave
2     paul
3     michael

|Table B|
key - value
1     dave
2     chris

问题很简单,用表 B 替换表 A,但要知道操作中哪些条目被插入到表 A 中或从表 A 中删除。

我的第一个(天真的)解决方案涉及分两个阶段使用表连接来生成中间列表,首先是删除操作,然后是插入操作。这些查询的结果存储在客户端上,并且是正确的应用程序功能所必需的。

SELECT * FROM A LEFT JOIN B ON A.value = B.value WHERE B.value IS NULL;
DELETE FROM A WHERE value IN ("paul", "michael");

SELECT * FROM B LEFT JOIN A ON A.value = B.value WHERE A.value IS NULL;
INSERT INTO A (value) VALUES "chris";

这种简单的方法在技术上确实有效,到事务表 A 的末尾将包含与表 B 相同的内容,但是这种策略很快变得相当慢。为了说明表的大小,它在数百万行的范围内,因此规模性能是一个关键因素,找到更优化的方法会很好。

为了满足性能要求,我计划调查以下内容:

  1. 使用 HStore 后端实现最佳键值存储性能。
  2. 使用视图预先计算中间删除/插入查询。
  3. 使用准备好的查询来减少 SQL 处理开销。

我向专家提出的问题是,您能否提出认为是最佳策略的建议。稍微超出我的问题范围,您可以提出任何硬性规定吗?

非常感谢您的参与。非常欢迎所有反馈。

4

3 回答 3

1

这并不完美,但它确实有效。这三个案例(删除、更新、插入)可能会组合成一个完整的外连接。

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE table_a (
        zkey INTEGER NOT NULL PRIMARY KEY
        , zvalue varchar NOT NULL
        , CONSTRAINT a_zvalue_alt UNIQUE (zvalue)
        );
INSERT INTO table_a(zkey, zvalue) VALUES
 (1, 'dave' )
,(2, 'paul' )
,(3, 'michael' )
        ;

CREATE TABLE table_b (
        zkey INTEGER NOT NULL PRIMARY KEY
        , zvalue varchar NOT NULL
        , CONSTRAINT b_zvalue_alt UNIQUE (zvalue)
        );
INSERT INTO table_b(zkey, zvalue) VALUES
(1, 'dave' )
,(2, 'chris' )
,(5, 'Arnold' )
        ;

CREATE TABLE table_diff (
        zkey INTEGER NOT NULL
        , zvalue varchar NOT NULL
        , opcode INTEGER NOT NULL DEFAULT 0
        );

WITH xx AS (
        DELETE FROM table_a aa
        WHERE NOT EXISTS (
                SELECT * FROM table_b bb
                WHERE bb.zkey = aa.zkey
                )
        RETURNING aa.zkey, aa.zvalue
        )
INSERT INTO table_diff(zkey,zvalue,opcode)
SELECT xx.zkey, xx.zvalue, -1
FROM xx
        ;

SELECT * FROM table_diff;

WITH xx AS (
        UPDATE table_a aa
        SET zvalue= bb.zvalue
        FROM table_b bb
        WHERE bb.zkey = aa.zkey
        AND bb.zvalue <> aa.zvalue
        RETURNING aa.zkey, aa.zvalue
        )
INSERT INTO table_diff(zkey,zvalue,opcode)
SELECT xx.zkey, xx.zvalue, 0
FROM xx
        ;
SELECT * FROM table_diff;

WITH xx AS (
        INSERT INTO table_a (zkey, zvalue)
        SELECT bb.zkey, bb.zvalue
        FROM table_b bb
        WHERE NOT EXISTS (
                SELECT * FROM table_a aa
                WHERE bb.zkey = aa.zkey
                AND bb.zvalue = aa.zvalue
                )
        RETURNING zkey, zvalue
        )
INSERT INTO table_diff(zkey,zvalue,opcode)
SELECT xx.zkey, xx.zvalue, 1
FROM xx
        ;
SELECT * FROM table_a;
SELECT * FROM table_b;
SELECT * FROM table_diff;

结果:

INSERT 0 3
CREATE TABLE
INSERT 0 1
 zkey | zvalue  | opcode 
------+---------+--------
    3 | michael |     -1
(1 row)

INSERT 0 1
 zkey | zvalue  | opcode 
------+---------+--------
    3 | michael |     -1
    2 | chris   |      0
(2 rows)

INSERT 0 1
 zkey | zvalue 
------+--------
    1 | dave
    2 | chris
    5 | Arnold
(3 rows)

 zkey | zvalue 
------+--------
    1 | dave
    2 | chris
    5 | Arnold
(3 rows)

 zkey | zvalue  | opcode 
------+---------+--------
    3 | michael |     -1
    2 | chris   |      0
    5 | Arnold  |      1
(3 rows)

顺便说一句:OQ 对要求非常模糊。如果 table_diff 是一个实际的历史表,则至少应该添加一个时间戳列,并且 zkey 和 ztimestamp 将是键的自然选择。此外,整个过程可以包含在一组规则或触发器中。

于 2012-11-22T23:25:12.637 回答
0

尝试使用此查询:

DELETE FROM A 
WHERE A.value NOT IN (SELECT B.value FROM B);

INSERT INTO A(value)
SELECT B.value
FROM B
WHERE B.value NOT IN (SELECT A.value FROM A)

使用索引A.valueB.value此查询将非常快。

于 2012-11-22T21:53:29.173 回答
0

如果您value在两个表中都建立了索引,并且value在每个表中都是唯一的,则这是完全外连接的情况,它应该能够通过遍历索引来合并两者:

SELECT CASE WHEN B.value IS NULL THEN
       'DELETE FROM A WHERE A.value = ' || quote_literal(A.value)
            ELSE
       'INSERT INTO A(value) VALUES(' || quote_literal(B.value) || ')'
       END
FROM A FULL OUTER JOIN B ON A.value = B.value
WHERE A.value IS DISTINCT FROM B.value

这里的 SQL 生成实际上只是为了演示查询的输出是什么。

于 2012-11-22T22:22:43.157 回答