2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SQL入門】INSERT INTO SELECT構文で既存データを使って一括登録する方法

Last updated at Posted at 2026-01-09

〜テーブル間コピー・履歴テーブル作成・集計データ反映などの実務ユースケース〜

:pencil2: はじめに

SQLで新しいデータを登録する際、INSERT INTO ... VALUES(...) を使うのが一般的ですが、
既存のテーブルデータをもとに新しいレコードを作りたいケースでは、
INSERT INTO SELECT構文 が非常に便利です。

この記事では、構文の基本から実務でのユースケースまで、分かりやすく解説します。

対応DB: PostgreSQL, MySQL, SQL Server, Oracle, SQLite 等、主要なRDBMS全般 ※本記事のサンプルコードは標準SQLおよびPostgreSQL(DATE_TRUNCなど)をベースに記載しています。

:pushpin: 基本構文

basic.sql
INSERT INTO 追加先テーブル (1, 2, 3, ...)
SELECT 1, 2, 3, ...
FROM 参照元テーブル
WHERE 条件;

ポイント

  • INSERT INTO の後に 追加先テーブルと列名 を指定する
  • SELECT 部分では 参照元テーブルのデータを抽出 する
  • WHERE 句で抽出条件を絞れる

:mag_right: ユースケース1:マスタからテンポラリテーブルを作る

開発やバッチ処理で「本番マスタを壊さないように一時テーブルを作りたい」ときに使えます。

usecase1.sql
INSERT INTO TEMP_CUSTOMER (CUSTOMER_ID, NAME, EMAIL)
SELECT CUSTOMER_ID, NAME, EMAIL
FROM CUSTOMER
WHERE IS_ACTIVE = TRUE;

:point_up: 有効な顧客だけを一時テーブルにコピーする例です。

:mag_right: ユースケース2:履歴テーブルへコピーする

「更新前の状態を履歴テーブルに保存する」ケースでもよく使われます。

usecase2.sql
INSERT INTO CUSTOMER_HISTORY (CUSTOMER_ID, NAME, UPDATED_AT)
SELECT CUSTOMER_ID, NAME, CURRENT_TIMESTAMP
FROM CUSTOMER
WHERE CUSTOMER_ID = 1001;

:point_up: 更新前に履歴を残すことで、変更追跡が可能になります。

:mag_right: ユースケース3:集計結果を別テーブルに格納する

バッチ処理やレポート生成などで、「集計結果を物理テーブルに保存」したいケースにも。

usecase3.sql
INSERT INTO MONTHLY_SALES (MONTH, TOTAL_AMOUNT)
SELECT DATE_TRUNC('month', ORDER_DATE), SUM(AMOUNT)
FROM ORDERS
GROUP BY DATE_TRUNC('month', ORDER_DATE);

:point_up: 月ごとの売上集計を MONTHLY_SALES テーブルに一括登録する例です。
:bulb: 補足 日付操作の関数はDB製品ごとに異なります。お使いのDB(MySQLなら DATE_FORMAT など)に合わせて書き換えてください。

:mag_right: ユースケース4:JOINを使って複数テーブルから挿入する

複数テーブルの結合結果を挿入することもできます。

usecase4.sql
INSERT INTO ORDER_DETAIL_SUMMARY (ORDER_ID, CUSTOMER_NAME, TOTAL_PRICE)
SELECT o.ORDER_ID, c.NAME, SUM(od.PRICE * od.QUANTITY)
FROM ORDERS o
JOIN ORDER_DETAIL od ON o.ORDER_ID = od.ORDER_ID
JOIN CUSTOMER c ON o.CUSTOMER_ID = c.CUSTOMER_ID
GROUP BY o.ORDER_ID, c.NAME;

:point_up: JOINで結合して、複数テーブルの情報をまとめて登録する例です。

:warning:パフォーマンス・注意点

  • INSERT INTO SELECTは大量データを一括で処理できるため、ループ処理より高速
  • 列数・型が一致していることを確認すること
  • 大量データを扱う場合、単に「遅い」だけでなく 「サービスが止まる」リスク があること。以下の3点は必ず意識しましょう。
    • 「インデックス・トリガー・制約」が重い
      • インデックス : 1行挿入するたびに、裏側で「索引」の並び替えが発生します。100万件あれば100万回の書き換えが発生するため、非常に時間がかかります。
      • 制約 (PRIMARY KEY/NOT NULLなど) : 挿入するデータが正しいか、他のテーブルを毎回見に行くため負荷がかかります。
      • 対策: 管理者や経験者に相談のうえ、一度インデックスを削除(DROP)し、挿入後に再作成(CREATE)する方が早い場合があります。
    • 「トランザクション・ログ」の肥大化
      • 大量の INSERT を一度に行うと、DBは「失敗した時に元に戻せるよう」に処理内容をメモリやログファイルに溜め込みます。
      • リスク : DBサーバーのディスク容量がいっぱいになり、他の処理も含めて停止する可能性があります。
      • 対策 : WHERE 句でIDの範囲を区切るなどして、数回に分けて実行することを検討してください。
    • テーブルロック(排他制御)
      • 処理中は、対象のテーブルが「編集中」としてロックされます。
      • リスク : 処理が終わるまで、他のユーザーがそのテーブルを検索したり更新したりできなくなる(待ちが発生する)ことがあります。

:stopwatch: まとめ

やりたいこと 書き方 補足
テーブルコピー INSERT INTO A SELECT * FROM B 条件で絞ることも可
履歴保存 INSERT INTO history SELECT ... FROM ... 更新前に実行
集計結果保存 INSERT INTO report SELECT SUM(...) FROM ... GROUP BY活用
JOINで統合 INSERT INTO summary SELECT ... FROM A JOIN B 複数テーブル対応

最後に

INSERT INTO SELECTは、データ加工・履歴管理・集計処理など
実務で最も出番の多いSQL構文の1つです。

「一括登録処理をループで書いていた」人は、
ぜひこの構文を使ってよりシンプルで高速なSQLに書き換えてみてください!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?