SQLのinsert selectの構文、各DBMSごとの使用例や独自拡張についてまとめています。
SQLのinsert selectの構文
insert~select文は、既存のテーブルからデータを取得し別テーブルにinsertします。構文は以下の通りです。
INSERT INTO テーブル名1 (カラム名,カラム名…) SELECT カラム名,カラム名… FROM テーブル名2 WHERE 抽出条件
以下は特定の行を抽出して、同じテーブルに別のキーでinsertする例です。テーブルemployees(社員)のemp_no(社員番号)が10001のデータを取得して、emp_noのみ3に置き換えてemployeesにinsertしています。
insert employees SELECT 3, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date` FROM `employees` WHERE emp_no = 10001
MySQLで実行するとこうなります。
Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> select * from employees limit 10; +--------+------------+------------+-----------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-----------+--------+------------+ | 3 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | | 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 | | 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 | | 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 | | 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 | | 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 | | 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 | | 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 | | 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 | +--------+------------+------------+-----------+--------+------------+
emp_no=10001のレコードが、emp_noのみ3に置き換わってinsertされています。
【関連記事】
▶【SQL】SELECTした結果を別テーブルへINSERTする方法
DBMSごとのinsert~selectの構文
Oracleのinsert~select構文
以下は、Oracleのinset~selectの例です。
INSERT INTO bonuses SELECT employee_id, salary*1.1 FROM employees WHERE commission_pct > 0.25;
Oracleでリモートデータベースへの挿入を行うには、テーブル名に@を指定します。簡易的なレプリケーションが可能です。
INSERT INTO employees@remote VALUES (8002, 'Juan', 'Fernandez', 'juanf@example.com', NULL, TO_DATE('04-OCT-1992', 'DD-MON-YYYY'), 'SH_CLERK', 3000, NULL, 121, 20);
SQL Serverのinsert~select構文
SQL Serverのinsert~selectの例です。
INSERT INTO dbo.EmployeeSales SELECT 'SELECT', sp.BusinessEntityID, c.LastName, sp.SalesYTD FROM Sales.SalesPerson AS sp INNER JOIN Person.Person AS c ON sp.BusinessEntityID = c.BusinessEntityID WHERE sp.BusinessEntityID LIKE '2%' ORDER BY sp.BusinessEntityID, c.LastName; GO
関連)INSERT (Transact-SQL) – SQL Server | Microsoft Docs
複数のリモートデータベースにinsert~selectでデータをコピーする場合、サブクエリのselect文でorder byを明示しないと、不整合が起こる可能性があります。大きなデータを扱う場合には注意しましょう。
MySQLのinsert~select構文
以下の例では、tbl_temp1のデータのうち、fid_order_idが100以上のデータのorder_idをtbl_temp2のfid_idにinsertします。
INSERT INTO tbl_temp2 (fld_id) SELECT tbl_temp1.fld_order_id FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;
INSERT IGNORE INTO…とIGNOREのIGNORE指定をすると、insert時の重複が無視されます。ただし、重複が解消されるわけではないのでデータベースとしては整合性が取れない状態になります。
参考)MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.5.1 INSERT … SELECT 構文
PostgreSQLのinsert~select構文
INSERT INTO films SELECT * FROM tmp_films WHERE date_prod < '2004-05-07';
WITHを指定するとinsert文で使用することのできるサブクエリを記述することが可能です。
WITH upd AS ( UPDATE employees SET sales_count = sales_count + 1 WHERE id = (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation') RETURNING * ) INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
WITHは、PostgreSQLの独自拡張です。
まとめ
- insert selectは、既存テーブルの内容を別テーブルに挿入するSQL
- insert先をリモートデータベースに指定することで、簡易的なレプリケーションが可能
- IGNORE指定で重複データが無視されるが、重複が解消されるわけではない