幸運にも現在のPostgreSQL 9.1を実行できる場合は、新しいデータ変更 CTEを使用した1 つのコマンドによるエレガントで高速なソリューションがあります。
データ変更 CTE は言うまでもなく、Common Table Expressions (CTE)をサポートしていないMySQLでは、そのような運はありません。
(col1, col2)
最初は一意であると仮定します。
クエリ 1
- この場合、テーブルから任意のスライスを簡単に選択できます。
- のシーケンス番号は
t.id
無駄になりません。
WITH s AS (
SELECT id, col1, col2
FROM t
-- WHERE some condition
)
,i AS (
INSERT INTO t (col1, col2)
SELECT col1, col2 -- I gather from comments that id is a serial column
FROM s
RETURNING id, col1, col2
)
INSERT INTO tu (t, u)
SELECT i.id, tu.u
FROM tu
JOIN s ON tu.t = s.id
JOIN i USING (col1, col2);
(col1, col2)
がunique でない場合、他に 2 つの方法があります。
クエリ 2
WITH s AS (
SELECT id, col1, col2
, row_number() OVER (PARTITION BY col1, col2) AS rn
FROM t
-- WHERE some condition
)
,i AS (
INSERT INTO t (col1, col2)
SELECT col1, col2
FROM s
RETURNING id, col1, col2
)
,r AS (
SELECT *
, row_number() OVER (PARTITION BY col1, col2) AS rn
FROM i
)
INSERT INTO tu (t, u)
SELECT r.id, tu.u
FROM r
JOIN s USING (col1, col2, rn) -- match exactly one id per row
JOIN tu ON tu.t = s.id;
クエリ 3
- これは、@ypercube が既に提供したのと同じアイデアに基づいていますが、すべて 1 つのクエリです。
- current の数値スペースに穴がある場合
t.id
、それに応じてシーケンス番号が新しい行に焼き付けられます。
- 新しい最大値を超えてシーケンスをリセットすることを忘れないでください。そうしないと、シーケンスから
t
デフォルトを描画する新しい挿入に対して重複キーエラーが発生しid
ます。これをコマンドの最終ステップとして統合しました。この方法が最も速く安全です。
WITH s AS (
SELECT max(id) AS max_id
FROM t
)
,i AS (
INSERT INTO t (id, col1, col2)
SELECT id + s.max_id, col1, col2
FROM t, s
)
,j AS (
INSERT INTO tu (t, u)
SELECT tu.t + s.max_id, tu.u
FROM tu, s
)
SELECT setval('t_id_seq', s.max_id + s.max_id)
FROM s;
マニュアルのsetval()に関する詳細。
テスト設定
簡単なテスト用。
CREATE TEMP TABLE t (id serial primary key, col1 text, col2 text);
INSERT INTO t (col1, col2) VALUES
('A', 'B')
,('C', 'D');
CREATE TEMP TABLE tu (t int, u int);
INSERT INTO tu VALUES
(1, 100)
,(1, 101)
,(2, 100)
,(2, 102);
SELECT * FROM t;
SELECT * FROM tu;
最近、やや似たような質問があり、やや似た回答を提供しました。CTE とウィンドウ関数のないバージョン8.3のプラスの代替。