SQL INSERT文の使い方|1行・複数行追加とINSERT INTO SELECT

記事の目的

SQLの INSERT 文を使って、テーブルに新しい行を追加できるようになります。

1行だけ入れる基本形に加えて、複数行をまとめて入れる書き方、ほかのテーブルから検索した結果をそのまま入れるINSERT INTO … SELECT 、自動採番(AUTO_INCREMENT・SERIAL)の扱い、そして「同じキーが既にあるときどうするか」までを、PostgreSQL・MySQL・SQL Server の製品差に注意しながら整理します。

記事のサンプルテーブルの前提

本記事では、説明用に次のような users テーブルがある前提で進めます。

  • id :自動採番(PostgreSQLなら SERIAL 、MySQLなら AUTO_INCREMENT 、SQL ServerならIDENTITY
  • name :氏名(文字列)
  • email :メールアドレス。重複を防ぐ UNIQUE 制約あり
  • created_at :作成日時。DEFAULT に現在日時が設定されている実際の CREATE TABLE 文は製品ごとに書き方が変わるため、別記事で扱います。
目次

最短の答え

users テーブルに1行追加する基本形は次のとおりです。

  • 主要なRDBMS(PostgreSQL / MySQL / SQL Server)で共通して使える基本形
INSERT INTO users (name, email)
VALUES ('佐藤', 'sato@example.com');

実行後、テーブルには新しい行が1件追加されます。

  • 複数行をまとめて入れたい場合は、VALUES の後ろにカンマ区切りで並べます。
INSERT INTO users (name, email) VALUES
 ('佐藤', 'sato@example.com'),
 ('鈴木', 'suzuki@example.com'),
 ('田中', 'tanaka@example.com');
  • 別のテーブルの検索結果をそのまま入れたい場合は SELECT を続けます。
INSERT INTO users_archive (name, email)
SELECT name, email FROM users WHERE created_at < '2025-01-01';

ここから先は、それぞれの書き方の意味と、製品ごとに引っかかりやすい点を順に解説します。

基本構文 ── INSERT INTO … VALUES

INSERT 文の最も基本的な形は次のとおりです。

INSERT INTO テーブル名 (列1, 列2, 列3)
VALUES (値1, 値2, 値3);
  • INSERT INTO の後ろに、データを入れたいテーブル名を書きます。
  • 続けてかっこ () の中に、値を入れたい列の名前を順に並べます。
  • VALUES の後ろのかっこ () の中に、その列に対応する値を同じ順番で並べます。

列名を省略するとどうなるか

— 動きはする。ただし非推奨
INSERT INTO users
VALUES (1, ‘佐藤’, ‘sato@example.com’);

省略した場合は「テーブル定義の左から順に値を並べる」という意味になります。後でテーブルに列が追加されたり順序が変わったりすると、想定しない列に値が入ってエラーになります。業務コードでは必ず列名を書きます。

NULL と DEFAULT

明示的に NULLDEFAULT (テーブル定義の既定値)を入れたい場合は、値の位置に書きます。

INSERT INTO users (name, email, memo)
VALUES ('佐藤', 'sato@example.com', NULL);

INSERT INTO users (name, email, created_at)
VALUES ('佐藤', 'sato@example.com', DEFAULT);

NOT NULL 制約のある列に NULL を入れようとするとエラーになります。逆に、NOT NULLDEFAULTも無い列を列リストから外すと、これも値が無くてエラーになります。

複数行INSERT ── 一度にまとめて入れる

同じテーブルに複数行を入れるなら、1行ずつ INSERT を繰り返すより、VALUES をカンマで並べて1回の INSERT にまとめるほうが高速です。ネットワーク往復が減り、トランザクションも1つにまとまります。

INSERT INTO users (name, email) VALUES
 ('佐藤', 'sato@example.com'),
 ('鈴木', 'suzuki@example.com'),
 ('田中', 'tanaka@example.com');

実行後の中身は次のようになります。

複数行INSERTの製品差

3製品とも複数行 VALUES の構文をサポートしますが、まとめて入れられる行数の感覚が異なります。

製品複数行
VALUES
1文あたりの目安
PostgreSQL対応明確な行数上限は通常意識しない(SQL文サイズ・メモリ・実行時間の制約は受ける)
MySQL対応max_allowed_packet (既定64MB 程度)までのSQL文サイズ
SQL Server対応1文あたり 1,000行まで(超えるとError 10738

SQL Server の1,000行制限は、INSERT … VALUES で複数行を直接並べた場合の制限です。これを超える場合は、INSERT を複数回に分けるか、BULK INSERTbcp 、あるいは派生テーブルからのINSERT … SELECT を使います。

いずれの製品でも、数万行を超えるデータは INSERT の繰り返しではなく専用のバルクロード手段を使うのが定石です(詳しくは後述の「落とし穴」)。

上記の数値はバージョンや構成で変わります。各製品の現行ドキュメントで再確認してください

INSERT INTO … SELECT ── 検索結果をそのまま入れる

別のテーブル(または同じテーブル)の検索結果を、そのまま新しい行として入れる書き方です。データの複製・アーカイブ・集計結果の保存などで頻繁に使います。

INSERT INTO users_archive (name, email, created_at)
SELECT name, email, created_at
FROM users
WHERE created_at < '2025-01-01';

ポイントは次の3つです。

  1. INSERT 側の列リストと SELECT 側の列の順序が一致していること。
  2. SELECT の列数と INSERT の列リストの数が一致していること。
  3. それぞれのデータ型が代入互換であること(数値→文字列のような暗黙変換は製品によって挙動が異なります)。

集計結果をそのまま保存する

SELECT 側は集計や JOIN を含む通常の SELECT 文がそのまま使えます。

-- 月次の購入件数をサマリーテーブルに記録する例
INSERT INTO purchase_monthly_summary (year_month, total_count, total_amount)
SELECT
 TO_CHAR(purchased_at, 'YYYY-MM') AS year_month, -- PostgreSQL の例
 COUNT(*),
 SUM(amount)
FROM purchases
WHERE purchased_at >= '2026-01-01' AND purchased_at < '2026-02-01'
GROUP BY TO_CHAR(purchased_at, 'YYYY-MM');

TO_CHAR は PostgreSQL の関数です。MySQL では DATE_FORMAT 、SQL Server では FORMAT またはCONVERT を使います。詳細は各製品の日付処理記事へ。

自動採番列(主キーの自動採番)

主キーを自動で振りたい場合、製品ごとに書き方が異なります。列名を省略するか、DEFAULT を入れるか、対応する関数を入れるのが基本です。

製品列定義の書き方INSERT時の指定
PostgreSQL(SQL標準準拠)
id BIGINT GENERATED ALWAYS AS IDENTITY

(従来表現)
id BIGSERIAL
列リストから外す or DEFAULT
MySQLid BIGINT AUTO_INCREMENT PRIMARY KEY列リストから外す orNULL
SQL Serverid BIGINT IDENTITY(1,1) PRIMARY KEY列リストから外す(直接指定は SET IDENTITY_INSERT が必要)

PostgreSQL 10以降は SQL標準準拠の GENERATED AS IDENTITY が推奨され、SERIAL / BIGSERIAL は従来から広く使われている書き方として現在も有効です。

-- PostgreSQL / MySQL / SQL Server で共通の書き方(id列を省略する)
INSERT INTO users (name, email)
VALUES ('佐藤', 'sato@example.com');

採番された値を取得する

INSERT 直後にその行の id を知りたい、というのは非常によくある要求ですが、書き方は完全に製品依存です。

-- PostgreSQL: RETURNING 句(標準SQL拡張)
INSERT INTO users (name, email)
VALUES ('佐藤', 'sato@example.com')
RETURNING id;
-- MySQL: 直近の AUTO_INCREMENT 値を関数で取得
INSERT INTO users (name, email) VALUES ('佐藤', 'sato@example.com');
SELECT LAST_INSERT_ID();
-- 注:LAST_INSERT_ID() は接続単位で管理されます。
-- INSERTを実行したのと「同じ接続」で取得してください。
-- 別の接続から呼ぶと、その接続で最後に行われた採番の値(または0)が返ります。
-- SQL Server: OUTPUT 句(推奨)
INSERT INTO users (name, email)
OUTPUT INSERTED.id
VALUES ('佐藤', 'sato@example.com');

PostgreSQL の RETURNING 句については、別記事で詳しく扱います。

重複時の挙動(製品別UPSERT)

主キーやUNIQUE 制約のある列に同じ値を入れると、エラーになります。

-- email 列に UNIQUE 制約がある場合、同じ email の再INSERTは失敗する
INSERT INTO users (name, email) VALUES ('佐藤', 'sato@example.com');
INSERT INTO users (name, email) VALUES ('別人', 'sato@example.com');
-- → 制約違反エラー

「既にあれば更新、無ければ追加」(UPSERT)にしたい場合、各製品が独自の構文を持ちます。ここでは入口だけ示します。それぞれの落とし穴・指定方法は別記事で扱います。

製品UPSERT の代表的な構文詳細
PostgreSQLINSERT … ON CONFLICT (列) DO UPDATE SET …PostgreSQL個別記事へ
MySQLINSERT … ON DUPLICATE KEY UPDATE 列 = 値MySQL個別記事へ
SQL Server(同時実行下の競合に注意)
MERGE
SQL Server個別記事へ
-- PostgreSQL の例(PostgreSQL 9.5以降)
INSERT INTO users (email, name)
VALUES ('sato@example.com', '佐藤(更新)')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name;

詳しくは別記事で扱います。

UPSERTの具体的な書き方は版差が大きい領域です(例:MySQL 8.0.20以降の構文変更、SQL Server
MERGE の挙動)。各製品の現行マニュアルで版を確認してください。

落とし穴・注意点

INSERTDELETEDROP のような破壊的操作ではありませんが、運用で問題になりがちな点があります。

トランザクションを意識する

3製品とも、既定では文ごとの自動コミット( autocommit )が有効です。この状態で複数の INSERT文を順に実行していると、途中でエラーになっても、すでに成功した文は確定済みになります。複数のINSERT をまとめて「失敗時はすべて戻したい」場合は、明示的にトランザクションで囲みます。書き方は製品で少し異なります。

製品明示的なトランザクションの書き方
PostgreSQLBEGIN; … COMMIT;
MySQLSTART TRANSACTION; … COMMIT;またはBEGIN; … COMMIT;
SQL ServerBEGIN TRANSACTION; … COMMIT;

途中で異常が起きたときは ROLLBACK; で巻き戻せます。

外部キー制約に注意

参照先のテーブルに対応する行が無いと、INSERT は失敗します。子テーブル → 親テーブル の順は逆で、親 → 子の順で入れるのが原則です。

トリガー・既定値で意図しない値が入ることがある

BEFORE INSERT トリガーや列の DEFAULT 式が、自分の知らない値を入れている場合があります。意図しない値が入っているときは、まずトリガー定義と列定義を確認します。

大量INSERTには専用の手段

数万行を超える挿入は、INSERT の繰り返しではなく COPY(PostgreSQL)/ LOAD DATA INFILE (MySQL)/ BULK INSERTbcp (SQL Server)を検討します。

文字コード・タイムゾーンの取り扱い

クライアントとサーバの文字コードがズレていると、文字列がそのまま文字化けして保存されます。SET client_encoding (PostgreSQL)/ SET NAMES(MySQL)/接続文字列のエンコーディング指定(SQL Server)を最初に確認します。

まとめ

INSERT 文は SQL の最も基本的な操作ですが、列名は省略しない・複数行はまとめる・主キーの採番と取得は製品依存・UPSERT は子記事で個別に学ぶ、の4点を押さえれば、現場のほとんどのケースに対応できます。

目次