データベースを扱う際、一度に複数のレコードを効率的に挿入したいと思うことはありませんか?一行ずつINSERT文を実行するのは時間がかかり、非効率的です。
この記事では、SQLで複数行のデータを一括で挿入する方法を、基本から応用まで初心者にもわかりやすく解説します。
複数行INSERTの基本概念

複数行INSERTとは
複数行INSERTとは、一つのINSERT文で複数のレコードを同時にテーブルに挿入する機能です。
複数行INSERTの利点:
- 処理速度の大幅な向上
- ネットワーク通信の回数を削減
- トランザクション処理の効率化
- コードの簡潔性向上
- データベースサーバーの負荷軽減
従来の方法との比較:
-- 従来の方法(非効率)
INSERT INTO employees (name, department) VALUES ('Alice', 'Sales');
INSERT INTO employees (name, department) VALUES ('Bob', 'Marketing');
INSERT INTO employees (name, department) VALUES ('Charlie', 'HR');
-- 複数行INSERT(効率的)
INSERT INTO employees (name, department)
VALUES
('Alice', 'Sales'),
('Bob', 'Marketing'),
('Charlie', 'HR');
なぜ複数行INSERTが重要なのか
パフォーマンスの改善:
- データベースエンジンの最適化機能を活用
- インデックス更新の効率化
- メモリ使用量の最適化
- ディスクI/Oの削減
開発効率の向上:
- コードの保守性向上
- エラーハンドリングの簡素化
- デバッグの容易さ
- 可読性の改善
基本的な複数行INSERT構文

標準的な構文
基本構文の形式:
INSERT INTO table_name (column1, column2, column3)
VALUES
(value1_1, value1_2, value1_3),
(value2_1, value2_2, value2_3),
(value3_1, value3_2, value3_3);
構文の詳細説明:
INSERT INTO
:挿入操作を開始するキーワードtable_name
:データを挿入するテーブル名(column1, column2, column3)
:挿入するカラムの指定VALUES
:値の開始を示すキーワード(value1_1, value1_2, value1_3)
:各レコードの値
カラム指定の方法
全カラム指定の場合:
INSERT INTO employees (employee_id, name, department, salary, hire_date)
VALUES
(1, 'Alice Johnson', 'Sales', 50000, '2024-01-15'),
(2, 'Bob Smith', 'Marketing', 55000, '2024-01-20'),
(3, 'Charlie Brown', 'HR', 52000, '2024-01-25');
一部カラム指定の場合:
INSERT INTO employees (name, department)
VALUES
('David Wilson', 'IT'),
('Eva Garcia', 'Finance'),
('Frank Miller', 'Operations');
カラム指定を省略する場合:
-- すべてのカラムを順番通りに指定
INSERT INTO employees
VALUES
(4, 'Grace Lee', 'Legal', 58000, '2024-02-01'),
(5, 'Henry Davis', 'Research', 60000, '2024-02-05');
実践的な使用例
社員データの一括登録
テーブル構造の定義:
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
department VARCHAR(50),
position VARCHAR(50),
salary DECIMAL(10,2),
hire_date DATE,
email VARCHAR(100)
);
複数社員の一括登録:
INSERT INTO employees (employee_id, name, department, position, salary, hire_date, email)
VALUES
(101, '田中 太郎', '営業部', '営業担当', 400000, '2024-04-01', 'tanaka@company.com'),
(102, '佐藤 花子', 'マーケティング部', 'マーケター', 450000, '2024-04-01', 'sato@company.com'),
(103, '鈴木 次郎', 'IT部', 'エンジニア', 500000, '2024-04-01', 'suzuki@company.com'),
(104, '高橋 美穂', '人事部', '人事担当', 420000, '2024-04-01', 'takahashi@company.com'),
(105, '渡辺 健一', '経理部', '経理担当', 430000, '2024-04-01', 'watanabe@company.com');
商品データの一括登録
ECサイトの商品登録例:
INSERT INTO products (product_name, category, price, stock_quantity, description)
VALUES
('ノートパソコン ThinkPad X1', 'エレクトロニクス', 150000, 50, '高性能ビジネスノートPC'),
('ワイヤレスマウス', 'コンピューター周辺機器', 3500, 200, 'Bluetooth対応マウス'),
('USB充電器', 'コンピューター周辺機器', 2800, 150, '急速充電対応'),
('モニターアーム', 'オフィス用品', 8500, 75, 'デュアルモニター対応'),
('エルゴノミックキーボード', 'コンピューター周辺機器', 12000, 100, '疲労軽減設計');
注文履歴の一括登録
注文データの複数挿入:
INSERT INTO orders (customer_id, product_id, quantity, order_date, total_amount, status)
VALUES
(1001, 101, 2, '2024-03-15', 7000, '処理中'),
(1002, 102, 1, '2024-03-15', 150000, '発送済み'),
(1003, 103, 3, '2024-03-16', 10500, '配送中'),
(1001, 104, 1, '2024-03-16', 8500, '処理中'),
(1004, 105, 2, '2024-03-17', 24000, '注文確定');
データベース別の対応状況

MySQL での複数行INSERT
MySQL の特徴:
- MySQL 3.22.5 以降で対応
- 非常に高速な処理が可能
- 大量データの挿入に最適化
MySQL 固有の機能:
-- ON DUPLICATE KEY UPDATE との組み合わせ
INSERT INTO employees (employee_id, name, department)
VALUES
(101, '田中 太郎', '営業部'),
(102, '佐藤 花子', 'マーケティング部')
ON DUPLICATE KEY UPDATE
name = VALUES(name),
department = VALUES(department);
INSERT IGNORE の使用:
-- 重複エラーを無視して挿入
INSERT IGNORE INTO employees (employee_id, name, department)
VALUES
(101, '田中 太郎', '営業部'),
(102, '佐藤 花子', 'マーケティング部');
PostgreSQL での複数行INSERT
PostgreSQL の特徴:
- PostgreSQL 8.2 以降で対応
- トランザクション処理に優れている
- ACID特性の厳密な実装
PostgreSQL 固有の機能:
-- RETURNING句を使用した挿入後の値取得
INSERT INTO employees (name, department, hire_date)
VALUES
('新入社員A', 'IT部', CURRENT_DATE),
('新入社員B', '営業部', CURRENT_DATE)
RETURNING employee_id, name;
CONFLICT 処理:
-- PostgreSQL 9.5以降
INSERT INTO employees (employee_id, name, department)
VALUES
(101, '田中 太郎', '営業部'),
(102, '佐藤 花子', 'マーケティング部')
ON CONFLICT (employee_id)
DO UPDATE SET
name = EXCLUDED.name,
department = EXCLUDED.department;
SQL Server での複数行INSERT
SQL Server の特徴:
- SQL Server 2008 以降で対応
- Table-Valued Parameters との組み合わせが強力
- 大量データ処理に最適化
SQL Server での基本構文:
INSERT INTO employees (name, department, salary)
VALUES
('Alice Johnson', 'Sales', 50000),
('Bob Smith', 'Marketing', 55000),
('Charlie Brown', 'HR', 52000);
Oracle での複数行INSERT
Oracle の特徴:
- Oracle 9i 以降で INSERT ALL 構文をサポート
- 複数テーブルへの同時挿入も可能
Oracle の INSERT ALL:
INSERT ALL
INTO employees (employee_id, name, department) VALUES (101, '田中', '営業')
INTO employees (employee_id, name, department) VALUES (102, '佐藤', 'IT')
INTO employees (employee_id, name, department) VALUES (103, '鈴木', '人事')
SELECT * FROM dual;
パフォーマンス最適化

大量データ挿入時の考慮事項
推奨される挿入サイズ:
- MySQL:1000〜5000行程度
- PostgreSQL:1000〜3000行程度
- SQL Server:1000行程度
- システムのメモリとCPUに応じて調整
バッチ処理の実装例:
-- 1000件ずつ分割して挿入
INSERT INTO large_table (col1, col2, col3)
VALUES
-- 1〜1000件目のデータ
(value1, value2, value3),
...
(value1000_1, value1000_2, value1000_3);
-- 次のバッチ
INSERT INTO large_table (col1, col2, col3)
VALUES
-- 1001〜2000件目のデータ
(value1001_1, value1001_2, value1001_3),
...
(value2000_1, value2000_2, value2000_3);
インデックスとの関係
インデックス更新の最適化:
-- 大量挿入前にインデックスを削除
DROP INDEX idx_employee_name ON employees;
-- 大量データ挿入
INSERT INTO employees (name, department, salary)
VALUES
-- 大量のデータ
(...);
-- インデックスを再作成
CREATE INDEX idx_employee_name ON employees (name);
トランザクション管理
効率的なトランザクション処理:
BEGIN TRANSACTION;
INSERT INTO employees (name, department)
VALUES
('Employee 1', 'Department A'),
('Employee 2', 'Department B'),
('Employee 3', 'Department C');
-- エラーチェック
IF @@ERROR = 0
COMMIT TRANSACTION;
ELSE
ROLLBACK TRANSACTION;
エラーハンドリングと対処法
よくあるエラーと解決方法
エラー1:主キー重複エラー
-- 問題のあるINSERT
INSERT INTO employees (employee_id, name)
VALUES
(101, '田中'),
(101, '佐藤'); -- 重複エラー
-- 解決方法1:事前チェック
INSERT INTO employees (employee_id, name)
SELECT * FROM (
VALUES (101, '田中'), (102, '佐藤')
) AS new_data(id, name)
WHERE NOT EXISTS (
SELECT 1 FROM employees WHERE employee_id = new_data.id
);
エラー2:データ型不一致
-- 問題のあるINSERT
INSERT INTO employees (employee_id, name, salary)
VALUES
(101, '田中', 'invalid_number'); -- 数値型にテキスト
-- 解決方法:正しいデータ型で指定
INSERT INTO employees (employee_id, name, salary)
VALUES
(101, '田中', 500000);
エラー3:カラム数の不一致
-- 問題のあるINSERT
INSERT INTO employees (employee_id, name)
VALUES
(101, '田中', '営業部'); -- カラム数が合わない
-- 解決方法:カラム数を一致させる
INSERT INTO employees (employee_id, name, department)
VALUES
(101, '田中', '営業部');
部分的な失敗への対処
エラー時の対応戦略:
-- 方法1:個別にINSERTしてエラーを特定
DECLARE @ErrorCount INT = 0;
BEGIN TRY
INSERT INTO employees (employee_id, name, department)
VALUES (101, '田中', '営業部');
END TRY
BEGIN CATCH
SET @ErrorCount = @ErrorCount + 1;
PRINT 'エラー: ' + ERROR_MESSAGE();
END CATCH;
-- 方法2:一時テーブルを使用した検証
CREATE TEMPORARY TABLE temp_employees AS
SELECT * FROM (
VALUES
(101, '田中', '営業部'),
(102, '佐藤', 'IT部')
) AS t(employee_id, name, department);
-- 検証後に本テーブルに挿入
INSERT INTO employees (employee_id, name, department)
SELECT employee_id, name, department
FROM temp_employees
WHERE employee_id NOT IN (SELECT employee_id FROM employees);
高度な活用方法
サブクエリとの組み合わせ
他のテーブルからのデータ取得:
INSERT INTO employee_summary (employee_id, name, total_sales)
SELECT
e.employee_id,
e.name,
COALESCE(SUM(s.amount), 0)
FROM employees e
LEFT JOIN sales s ON e.employee_id = s.employee_id
WHERE e.department = '営業部'
GROUP BY e.employee_id, e.name;
複数テーブルからの複合データ挿入:
INSERT INTO monthly_report (employee_id, month, sales_count, total_amount)
SELECT
e.employee_id,
'2024-03',
COUNT(o.order_id),
SUM(o.total_amount)
FROM employees e
LEFT JOIN orders o ON e.employee_id = o.sales_person_id
AND DATE_FORMAT(o.order_date, '%Y-%m') = '2024-03'
GROUP BY e.employee_id;
条件付き挿入
CASE文を使った条件分岐:
INSERT INTO employee_categories (employee_id, category, bonus_rate)
VALUES
(101,
CASE WHEN (SELECT salary FROM employees WHERE employee_id = 101) > 500000
THEN 'High' ELSE 'Standard' END,
CASE WHEN (SELECT department FROM employees WHERE employee_id = 101) = '営業部'
THEN 0.15 ELSE 0.10 END
);
動的SQL生成
プログラムからの動的INSERT生成:
-- 疑似コード(実際の実装は言語により異なる)
DECLARE @SQL NVARCHAR(MAX) = 'INSERT INTO employees (name, department) VALUES ';
DECLARE @Values NVARCHAR(MAX) = '';
-- ループ処理でVALUES句を構築
FOR each employee IN employee_list:
SET @Values = @Values + '(''' + employee.name + ''', ''' + employee.department + '''), ';
-- 最後のカンマを削除
SET @Values = LEFT(@Values, LEN(@Values) - 1);
SET @SQL = @SQL + @Values;
-- 実行
EXEC sp_executesql @SQL;
セキュリティの考慮事項

SQLインジェクション対策
パラメータ化クエリの使用:
-- 危険な例(SQLインジェクションの脆弱性あり)
-- 文字列連結でクエリを作成するのは危険
-- 安全な例(パラメータ化クエリ)
PREPARE stmt FROM 'INSERT INTO employees (name, department) VALUES (?, ?)';
SET @name1 = 'Alice', @dept1 = 'Sales';
SET @name2 = 'Bob', @dept2 = 'Marketing';
EXECUTE stmt USING @name1, @dept1;
EXECUTE stmt USING @name2, @dept2;
DEALLOCATE PREPARE stmt;
権限管理
適切な権限設定:
-- 挿入専用ユーザーの作成
CREATE USER 'insert_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT INSERT ON database_name.employees TO 'insert_user'@'localhost';
GRANT SELECT ON database_name.departments TO 'insert_user'@'localhost';
まとめ
SQLでの複数行INSERT について、重要なポイントをまとめます:
基本的な構文:
- VALUES句:複数のレコードをカンマ区切りで指定
- カラム指定:挿入するカラムを明示的に指定
- データ型一致:各値が対応するカラムのデータ型と一致
- 構文統一:すべてのレコードで同じカラム数を維持
実践的なポイント:
- データベース別の対応状況を確認
- 適切なバッチサイズでの分割処理
- トランザクション管理の重要性
- エラーハンドリングの実装
パフォーマンス最適化:
- インデックス更新の考慮
- メモリ使用量の監視
- ネットワーク負荷の軽減
- 実行計画の確認
セキュリティ対策:
- SQLインジェクション防止
- 適切な権限管理
- データ検証の実装
- ログ記録の設定
コメント