2.

SQL INSERT 文 完全ガイド(カラム指定 / 複数行 / INSERT SELECT / UPSERT)

編集
この記事の要点
  • INSERT 文はテーブルにレコードを新規追加する DML。基本構文は INSERT INTO 表名 (列...) VALUES (値...)
  • 複数行: VALUES (...), (...), (...) をカンマ区切りで一括挿入 — ループ INSERT より数十倍高速
  • 別表からコピー: INSERT INTO ... SELECT ... FROM ... で SELECT 結果を流し込み
  • UPSERT: MySQL は INSERT ... ON DUPLICATE KEY UPDATE、PostgreSQL は INSERT ... ON CONFLICT ... DO UPDATE
  • トランザクション: COMMIT で確定、ROLLBACK で取り消し(オートコミット OFF 前提)

SQL INSERT 文とは

INSERT 文はテーブルに新しいレコードを追加するための DML (Data Manipulation Language) です。SELECT / UPDATE / DELETE と並ぶ SQL の最も基本的なコマンドのひとつ。

基本構文

INSERT INTO テーブル名 (
    カラム名1,
    カラム名2,
    ...
) VALUES (
    値1,
    値2,
    ...
);

カラム名と値は順序が対応します(カラム名1 に 値1 が入る)。

例: 1 行挿入

INSERT INTO test_table (
    id,
    name
) VALUES (
    1,
    'tarou'
);
-- Query OK, 1 row affected (0.01 sec)

カラム指定を省略する書き方

カラム名を省略するとテーブル定義の全カラムに対して順番に値を入れる必要があります。可読性 + 保守性が落ちるので本番コードでは非推奨

-- 全カラム順に
INSERT INTO test_table VALUES (1, 'tarou');

-- これは「id, name, created_at, ...」順に全部入れる
-- カラム追加時に既存 SQL が壊れるリスクあり

複数行を一気に INSERT

VALUES をカンマで複数並べると1 文で複数行を挿入できます。ループで INSERT を繰り返すより数十倍高速

INSERT INTO users (id, name, age) VALUES
    (1, 'tarou', 20),
    (2, 'hanako', 22),
    (3, 'jiro', 30),
    (4, 'mai', 28);
-- Query OK, 4 rows affected (0.01 sec)
-- Records: 4  Duplicates: 0  Warnings: 0

MySQL は1 文 65,535 プレースホルダ制限。バルクインサート 1 万件は問題なく可能。

INSERT ... SELECT

別テーブル(または同テーブル)の SELECT 結果をそのまま流し込む形。データ移行・履歴テーブル作成・スナップショットに頻出。

-- users から論理削除済みのものを users_archive にコピー
INSERT INTO users_archive (id, name, deleted_at)
SELECT id, name, NOW()
FROM users
WHERE delete_flag = 1;

-- 同じテーブル内でも可(id 重複に注意)
INSERT INTO orders (user_id, amount, created_at)
SELECT user_id, amount, NOW()
FROM orders
WHERE order_type = 'recurring';

UPSERT (重複時更新)

主キー / UNIQUE 制約に該当する行があれば UPDATE、なければ INSERT を 1 文で行う構文。製品ごとに記法が違います。

DB 製品構文
MySQL / MariaDBINSERT ... ON DUPLICATE KEY UPDATE
PostgreSQLINSERT ... ON CONFLICT (col) DO UPDATE
SQL ServerMERGE INTO ... USING ...
OracleMERGE INTO ... USING ...
SQLiteINSERT ... ON CONFLICT ... DO UPDATE
-- MySQL
INSERT INTO inventory (sku, qty) VALUES ('A001', 10)
ON DUPLICATE KEY UPDATE qty = qty + 10;

-- PostgreSQL
INSERT INTO inventory (sku, qty) VALUES ('A001', 10)
ON CONFLICT (sku) DO UPDATE SET qty = inventory.qty + EXCLUDED.qty;

挿入された ID を取得する

AUTO_INCREMENT で割り当てられた ID を取得する方法は製品ごとに違います。

-- MySQL / MariaDB
INSERT INTO users (name) VALUES ('tarou');
SELECT LAST_INSERT_ID();

-- PostgreSQL
INSERT INTO users (name) VALUES ('tarou')
RETURNING id;

-- SQL Server
INSERT INTO users (name) VALUES ('tarou');
SELECT SCOPE_IDENTITY();

トランザクション

INSERT 文を発行した後、COMMIT で確定し、ROLLBACK で取り消しできます。デフォルトでオートコミット ON の環境ではすぐに確定する点に注意。

-- オートコミット OFF にして明示的に管理
START TRANSACTION;
INSERT INTO users (id, name) VALUES (1, 'tarou');
INSERT INTO users (id, name) VALUES (2, 'hanako');
-- 何か間違えたら
ROLLBACK;
-- 問題なければ
COMMIT;

パフォーマンス Tips

Tips効果
複数行 INSERT (VALUES (), (), ...)1 行ずつより 10〜100 倍速
大量挿入時はLOAD DATA INFILE (MySQL) / COPY (PG)更に 10 倍速
INSERT 中にインデックスを一時 DISABLE大量挿入完了後に再構築するほうが速い
トランザクション内でまとめて COMMIT1 件ずつ COMMIT より速い
外部キーが多い場合は親→子の順制約違反エラー回避

FAQ

Q: INSERT で値の一部だけ指定したい
A: カラム名を明示すれば残りは DEFAULT 値 / NULL になる。INSERT INTO users (name) VALUES ('tarou')

Q: 重複 INSERT を無視したい (エラーで止めたくない)
A: MySQL は INSERT IGNORE、PostgreSQL は ON CONFLICT DO NOTHING

Q: 速い順は何?
A: LOAD DATA / COPY > 複数行 INSERT > プリペアドステートメント反復 > 1 件ずつ INSERT。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. SELECT
  2. INSERT

最近更新/作成されたページ