2017-09-02 4 views
0

接合テーブルproducts_tagsの2つのテーブルproducttagがあります。製品にはさまざまなタグがあり、タグにはさまざまな製品がありますが、1つの製品と1つのタグの関係は一意である必要があります。接合テーブルの更新

CREATE TABLE product (
    id serial PRIMARY KEY NOT NULL, 
    name varchar(255) NOT NULL 
) 

CREATE TABLE tag (
    id serial PRIMARY KEY NOT NULL, 
    name varchar(255) NOT NULL 
) 

CREATE TABLE products_tags (
    product_id integer NOT NULL REFERENCES product(id), 
    tag_id integer NOT NULL REFERENCES tag(id), 
    PRIMARY KEY(product_id, tag_id) 
) 
私は現在、製品のオブジェクトと、それがフロントエンド層により更新されるたびに tags配列を送信することにより、接合テーブルを更新する予定しております

は、現在のproduct_idが含まれているproducts_tags内のすべての行を削除し、新しい行を作成tags配列内の各IDについてproducts_tagsになります。

私の質問です:これを達成するより効率的な方法はありますか?または、ジャンクション表のデータ保全性を保証するために行を削除して再挿入するのは唯一の方法ですか?

+0

変更が最小限である(影響を受けた行が2 * Nかかる可能性がある)場合でも、行バージョンが多すぎると作成されます。 DBテーブルにないものを挿入し、新しいリストにないものを削除してください。 – wildplasser

+0

あなたは正しい@wildplasserです。私は、これを処理するpostgres関数を作成すると思います。アプリケーション層でそれをやって、DBへのラウンドトリップが多すぎると面倒なように感じるかもしれません。 – Toni

+0

ここでは機能は必要ありません。チェーン化されたCTEを使用して両方の操作を組み合わせることができます。そして、それを準備された声明の中に入れるかもしれない。 – wildplasser

答えて

1

これに苦しんでいる人にとっては、これが私の結末です。おそらくまだ最適化できますが、当初の質問よりはるかに優れた解決策です。

WITH insert_new_tags AS (
    INSERT INTO products_tags (product_id, tag_id) 
     VALUES (2, 1), (2, 2), (2, 7) 
     ON CONFLICT (product_id, tag_id) 
     DO NOTHING 
    RETURNING * 
), 
delete_missing_tags AS (
    DELETE FROM products_tags 
    WHERE product_id = 2 AND tag_id NOT IN (1, 2, 7) 
    RETURNING * 
) 

SELECT *, 'inserted' AS operation FROM insert_new_tags 
UNION 
SELECT *, 'deleted' AS operation FROM delete_missing_tags 

私を正しい方向に向けるための@wildplasserへの大きなtnks!