18

次の形式のテーブルが 2 つあります (つまり、すべての foo が正確に 1 つのバーにリンクされています)。

CREATE TABLE foo (
    id INTEGER PRIMARY KEY,
    x INTEGER NOT NULL,
    y INTEGER NOT NULL,
    ...,
    bar_id INTEGER UNIQUE NOT NULL,
    FOREIGN key (bar_id) REFERENCES bar(id)
);

CREATE TABLE bar (
    id INTEGER PRIMARY KEY,
    z INTEGER NOT NULL,
    ...
);

fooネストされたクエリを使用して、特定の条件を満たす行を簡単にコピーできます。

INSERT INTO foo (...) (SELECT ... FROM foo WHERE ...)

barしかし、関連する行のコピーを行ごとに作成しfoo、のIDをbar新しい行に挿入する方法がわかりませんfoo。単一のクエリでこれを行う方法はありますか?

望ましい結果の具体例:

-- Before query:

foo(id=1,x=3,y=4,bar_id=100)  .....  bar(id=100,z=7)
foo(id=2,x=9,y=6,bar_id=101)  .....  bar(id=101,z=16)
foo(id=3,x=18,y=0,bar_id=102) .....  bar(id=102,z=21)


-- Query copies all pairs of foo/bar rows for which x>3:

-- Originals
foo(id=1,x=3,y=4,bar_id=101)  .....  bar(id=101,z=7)
foo(id=2,x=9,y=6,bar_id=102)  .....  bar(id=102,z=16)
foo(id=3,x=18,y=0,bar_id=103) .....  bar(id=103,z=21)

-- "Copies" of foo(id=2,...) and foo(id=3,...), with matching copies of
-- bar(id=102,...) and bar(id=103,...)
foo(id=4,x=9,y=6,bar_id=104)  .....  bar(id=104,z=16)
foo(id=5,x=18,y=0,bar_id=105) .....  bar(id=105,z=21)
4

2 に答える 2

30

最終版

... OPからの追加情報の後。次のデモを検討してください。

-- DROP TABLE foo; DROP TABLE bar;

CREATE TEMP TABLE bar (
 id serial PRIMARY KEY  -- using a serial column!
,z  integer NOT NULL
);

CREATE TEMP TABLE foo (
 id     serial PRIMARY KEY  -- using a serial column!
,x      integer NOT NULL
,y      integer NOT NULL
,bar_id integer UNIQUE NOT NULL REFERENCES bar(id)
);

値を挿入します -bar最初に。このように質問にテストデータを提供し
ていただけると大変助かります。

INSERT INTO bar (id,z) VALUES
 (100, 7)
,(101,16)
,(102,21);

INSERT INTO foo (id, x, y, bar_id) VALUES
 (1, 3,4,100)
,(2, 9,6,101)
,(3,18,0,102);

シーケンスを現在の値に設定しないと、重複キー違反が発生します。

SELECT setval('foo_id_seq', 3);
SELECT setval('bar_id_seq', 102);

チェック:

-- SELECT nextval('foo_id_seq')
-- SELECT nextval('bar_id_seq')
-- SELECT * from bar;
-- SELECT * from foo;

クエリ:

WITH a AS (
    SELECT f.x, f.y, bar_id, b.z
    FROM   foo f
    JOIN   bar b ON b.id = f.bar_id
    WHERE  x > 3
    ),b AS (
    INSERT INTO bar (z)
    SELECT z
    FROM   a
    RETURNING z, id AS bar_id
    )
INSERT INTO foo (x, y, bar_id)
SELECT a.x, a.y, b.bar_id
FROM   a
JOIN   b USING (z);

これは、最後の更新で説明されていることを行う必要があります。

クエリは、 であると想定していzますUNIQUE。が一意でない場合zは、より複雑になります。この場合のウィンドウ関数を使用した解決策については、この関連する回答のクエリ 2 を参照してくださいrow_number()

また、との1 対 1 の関係を1つの結合テーブルに置き換えることも検討してください。foobar


データ変更 CTE

詳細情報の後の2番目の回答。

単一のクエリに行を追加する場合は、PostgreSQL 9.1 以降のfoo データ 変更CTE使用できます。bar

WITH x AS (
    INSERT INTO bar (col1, col2)
    SELECT f.col1, f.col2
    FROM   foo f
    WHERE  f.id BETWEEN 12 AND 23 -- some filter
    RETURNING col1, col2, bar_id  -- assuming bar_id is a serial column
    )
INSERT INTO foo (col1, col2, bar_id)
SELECT col1, col2, bar_id
FROM   x;

から値を引き出しfoo、に挿入しbar、自動生成されたものと一緒に返して、それを にbar_id挿入fooます。他のデータも使用できます。

これは、sqlfiddle で遊ぶための実用的なデモです


基本

明確にする前の基本的な情報を含む元の回答。
基本的な形式は次のとおりです。

INSERT INTO foo (...)
SELECT ... FROM foo WHERE ...

括弧は必要ありません。どのテーブルでも同じことができます

INSERT INTO foo (...)
SELECT ... FROM bar WHERE ...

そして、SELECT で挿入したテーブルに結合できます。

INSERT INTO foo (...)
SELECT f.col1, f.col2, .. , b.bar_id
FROM   foo f
JOIN   bar b USING (foo_id);  -- present in foo and bar

これは、挿入先のテーブルを含めることができる、他のものと同様の単なる SELECT です。行は最初に読み取られ、次に挿入されます。

于 2012-05-06T16:23:05.250 に答える