5

私には、コードをsqliteからpostgresに切り替える責任があります。私が問題を抱えているクエリの1つを以下にコピーします。

INSERT INTO group_phones(group_id, phone_name)
SELECT g.id, p.name 
FROM phones AS p, groups as g
WHERE g.id IN ($add_groups) AND p.name IN ($phones);

重複するレコードがある場合に問題が発生します。この表では、両方の値の組み合わせが一意である必要があります。他の場所でいくつかのplpgsql関数を使用して更新または挿入操作を実行しましたが、この場合、一度に複数の挿入を実行できます。このためのストアドルーチンの書き方がわかりません。そこにあるすべてのSQLの達人からのすべての助けに感謝します!

4

2 に答える 2

12

3つの課題があります。

  1. クエリにはテーブルとの間のJOIN条件がないため、これは事実上制限されます。これはおそらく意図しないことです。つまり、対象となるすべての電話は、対象となるすべてのグループと組み合わされます。100台の電話と100のグループがあり、すでに10,000の組み合わせになっている場合。phonesgroupsCROSS JOIN

  2. の異なる組み合わせを挿入します(group_id, phone_name)

  3. テーブルにすでに存在する行を挿入しないでくださいgroup_phones

それが次のように見えると考えられるすべてのもの:

INSERT INTO group_phones(group_id, phone_name)
SELECT i.id, i.name
FROM  (
    SELECT DISTINCT g.id, p.name -- get distinct combinations
    FROM   phones p
    JOIN   groups g ON ??how are p & g connected??
    WHERE  g.id IN ($add_groups)
    AND    p.name IN ($phones)
    ) i
LEFT   JOIN group_phones gp ON (gp.group_id, gp.phone_name) = (i.id, i.name)
WHERE  gp.group_id IS NULL  -- avoid duping existing rows

並行性

この形式は、同時書き込み操作による競合状態の可能性を最小限に抑えます。テーブル の同時書き込み負荷が大きい場合は、テーブルを排他的にロックするか、シリアル化可能なトランザクション分離を使用することをお勧めします。これにより、制約検証(行ありません)およびクエリの書き込み操作。

BEGIN ISOLATION LEVEL SERIALIZABLE;
INSERT ...
COMMIT;

シリアル化エラーでロールバックした場合は、トランザクションを繰り返す準備をしてください。このトピックの詳細については、@depeszによるこのブログ投稿またはSOに関するこの関連質問が出発点として適しています。

ただし、通常は、これを気にする必要はありません。

パフォーマンス

LEFT JOIN tbl ON right_col = left_col WHERE right_col IS NULL

一般に、右側のテーブルに個別の列がある最速の方法です。列に重複がある場合(特に多数ある場合)、

WHERE NOT EXISTS (SELECT 1 FROM tbl WHERE right_col = left_col)

最初の行が見つかるとすぐにスキャンを停止できるため、高速になる可能性があります。

IN@dezsoが示すように、を使用することもできますが、PostgreSQLでは通常低速です。

于 2012-08-09T21:10:15.597 に答える
2

次のことを試してください。

INSERT INTO group_phones(group_id, phone_name)
SELECT DISTINCT g.id, p.name 
FROM phones AS p, groups as g
WHERE 
    g.id IN ($add_groups) 
    AND p.name IN ($phones)
    AND (g.id, p.name) NOT IN (
        SELECT group_id, phone_name
        FROM group_phones
    )
;

を使用DISTINCTすると、一意の行が確実に挿入され、NOT IN句を使用すると、既存の行を除外できます。

このソリューションはおそらく理解しやすいでしょうが、ほとんどの場合、Erwinの方がパフォーマンスが向上します。

于 2012-08-09T21:09:16.937 に答える