🗃️

PostgreSQLで関連テーブルに複数行のレコードを挿入する

2021/10/01に公開

背景

あるテーブル parent_table のレコードを挿入するとき、関連テーブル child_table にも同時にレコードを挿入したいことはよくあると思います。
今回、後から機能追加により関連テーブルを追加したのですが、既存の parent_table に対してデフォルト値となる child_table を複数行挿入したいというシーンがあり、パッと方法が分からなかったため調べました。

VALUES リストを使う方法

INSERT INTO ... SELECT ... を使ってレコードを挿入するのですが、 SELECT するデータとして parent_table と PostgreSQL の VALUES リストCROSS JOIN します。

INSERT INTO "child_table" (
  "parent_table_id",
  "name"
) SELECT
  "id",
  "default_child_table"."name"
FROM "parent_table"
CROSS JOIN (VALUES 
  ('子レコード1'),
  ('子レコード2'),
  ('子レコード3')
) AS "default_child_table"("name");

SELECT に対して WHERELIMIT を追加することも可能ですので、大量のレコードを挿入する場合は適宜レコード数を調整しましょう。

一時的なテーブルを使う方法

CROSS JOIN を使うアイディアはあったのですがインラインで定数を定義する方法がわからず、一時的なテーブルを使う方法がまず思いつきました。
これでもやっていることは全く同じなのですが、一時的な作業のためにテーブルを作成するのは少し大掛かりですので、もっとシンプルなやりかたを探したところ前述の VALUES リストを使った方法にたどり着きました。
パフォーマンス上の違いなどは詳しく比較しておりません。

CREATE TEMPORARY TABLE "default_child_table" (
  "name" text
);

INSERT INTO "default_child_table" VALUES
  ('子レコード1'),
  ('子レコード2'),
  ('子レコード3');

INSERT INTO "child__table" (
  "parent_table_id",
  "name"
) SELECT
  "id",
  "default_child_table"."name"
FROM "parent_table"
CROSS JOIN "default_review_questions";

DROP TABLE "default_child_table";

まとめ

継続的にサービスの運用をしていると「あれ、簡単だと思ったら意外と難しいぞ?」ということはよくあるのですが、今回は使いどころが多そうなので記事として残しました。
もっといい方法を知っている型がいらっしゃればコメントを頂ければ嬉しいです。

Discussion