次の形式の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
の関連する行のコピーを作成し、bar
のIDを新しい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)
... 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
を最初に。
このような質問でテストデータを提供した場合、非常にhelpfulになります。
_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
が一意でない場合、より複雑になります。この場合のウィンドウ関数row_number()
を使用する準備ができたソリューションについては、 この関連する回答のクエリ2 を参照してください。
また、foo
とbar
の1:1の関係を単一の結合テーブルに置き換えることを検討してください。
詳細情報の後の2番目の回答。
単一のクエリでfoo
andbar
に行を追加する場合は、 データ修正CTE を使用できます。 = PostgreSQLから9.1:
_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
_と一緒に返して、thatを挿入しますfoo
に。他のデータも使用できます。
明確化する前の基本情報を含む元の回答。
基本的な形式は次のとおりです。
_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と同じです。挿入するテーブルを含めることができます。行が最初に読み込まれ、次に挿入されます。
id
のbar
がシリアルでデフォルト値がnextval('bar_id_seq'::regclass)
の場合、この関数を手動で呼び出してcteで新しいIDを取得できます。
with
s_bar as (
SELECT id, z, nextval('bar_id_seq'::regclass) new_id
FROM bar
WHERE ...
),
s_foo as (
SELECT x, y, bar_id
FROM foo
WHERE ...
),
i_bar as (
INSERT INTO bar (id, z)
SELECT new_id, z
FROM s_bar
),
i_foo as (
INSERT INTO foo (x, y, bar_id)
SELECT f.x, f.y, b.new_id
FROM s_foo f
JOIN s_bar b on b.id = f.bar_id
)
SELECT 1