0

私は PostgreSQL の初心者だと思われるかもしれません。この質問の目的は、この問題に対して PostgreSQL から最高のパフォーマンスを引き出す方法について洞察を得ることです。構造は同じですが、内容が異なる 2 つのテーブルがあります。

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

|Table B|
key - value
1     dave
2     chris

問題は単純で、テーブル A をテーブル B に置き換えますが、操作でどのエントリがテーブル A に挿入またはテーブル A から削除されたかを知る必要があります。

私の最初の (素朴な) 解決策は、テーブル結合を使用して 2 段階で作業を行い、最初に削除操作、次に挿入操作の中間リストを生成することです。これらのクエリの結果はクライアントに保存され、アプリケーションが正しく機能するために必要です。

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

これは完璧ではありませんが、機能します。これら 3 つのケース (削除、更新、挿入) は、完全な外部結合に結合される可能性があります。

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.valueと、B.valueこのクエリは非常に高速になります。

于 2012-11-22T21:53:29.173 に答える
0

value両方のテーブルでインデックスを作成し、各テーブルで一意である場合value、これは完全な外部結合のケースであり、インデックスをたどることで 2 つをマージできるはずです。

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 に答える