SQLのwith句を使ったupdateについてまとめています。
SQLのwith~updateの文法
with句はサブクエリを簡潔に記述することができます。
【関連記事】
▶SQLのwith句のサンプル サブクエリを共通テーブル式(CTE)を使って、単純なクエリで記述
以下は、MySQLにて、employees(社員)テーブルの値を取得するサブクエリをwithで定義して、departmetns2(部署)テーブルを更新する例です。
WITH val AS (SELECT first_name FROM employees WHERE emp_no = "10001") UPDATE departments2 SET dept_name = (SELECT * FROM val) WHERE dept_no = "d001"; mysql> select * from departments2; +---------+--------------------+ | dept_no | dept_name | +---------+--------------------+ | d009 | Customer Service | | d005 | Development | | d002 | Finance | | d001 | Georgi | | d003 | Human Resources | | d004 | Production | | d006 | Quality Management | | d008 | Research | | d007 | Sales | +---------+--------------------+ 9 rows in set (0.00 sec)
社員番号10001の社員のfirst_name(姓名の名)、「Georgi」を、部署テーブルの部署番号d001の値としてupdateしています。更新後のselect文で、更新内容を確認しています。
Oracleのwithを使ったupdate文
UPDATE TESTTBL SET STR1 = '1',NUM1 = 1 WHERE STR1 = ( WITH A AS ( SELECT '1' AS STR1 , 1 AS NUM1 FROM DUAL ) SELECT STR1 FROM A )
参考)社内SEの徒然なる日記 SQL(Oracle)のUPDATE文でWITH句を使う!
Oracleでも、MySQLのようにwithを使ってupdate文を記述することが可能です。上記の例のように、サブクエリ部分にwithを記述することもできますが、この使い方だとwithを使うメリットがあまりないかも知れません。
Oracleでは、他のDBMSでは動作するコードも、記述方法によってはSyntax Errorとなるケースがあるようです。
SQL Serverのwithを使ったupdate文
USE tempdb; GO -- UPDATE statement with CTE references that are correctly matched. DECLARE @x TABLE (ID INT, Value INT); DECLARE @y TABLE (ID INT, Value INT); INSERT @x VALUES (1, 10), (2, 20); INSERT @y VALUES (1, 100),(2, 200); WITH cte AS (SELECT * FROM @x) UPDATE x -- cte is referenced by the alias. SET Value = y.Value FROM cte AS x -- cte is assigned an alias. INNER JOIN @y AS y ON y.ID = x.ID; SELECT * FROM @x; GO ID Value ------ ----- 1 100 2 200 (2 row(s) affected)
関連)UPDATE (Transact-SQL) – SQL Server | Microsoft Docs
上記は、テーブル@xの値を、結合した別テーブル@yの値で更新する例です。withで定義した共通テーブル式に別名定義(cte AS x)を使う場合、共通テーブル式の記述場所全てに別名を使う必要があります。
MySQLのwithを使ったupdate文
WITH val AS (SELECT first_name FROM employees WHERE emp_no = "10001") UPDATE departments2 SET dept_name = (SELECT * FROM val) WHERE dept_no = "d001";
WITHを使ったupdate文は、サブクエリに書き直すことが可能です。以下は、setの値指定部分にサブクエリを使った例です。
update departments2 set dept_name=(select first_name from employees where emp_no="10001") where dept_no="d001";
updateのテーブル指定箇所でサブクエリを使う場合は以下のようになります。
mysql> update departments2, (select first_name from employees where emp_no="10001") tbl2 set dept_name=tbl2.first_name where dept_no="d001";
【関連記事】
▶MySQLのupdateでselectした結果を使う方法 サブクエリで自己テーブル更新も可能
いずれも結果は同じで、departmets2(部署)テーブルのd001のdept_name(部署名)が、employees(社員)テーブルのemp_no(社員番号)10001のfirst_name(姓名の名)、「Georgi」で更新されます。
select * from departments2 order by dept_no; +---------+--------------------+ | dept_no | dept_name | +---------+--------------------+ | d001 | Georgi | | d002 | Finance | | d003 | Human Resources | | d004 | Production | | d005 | Development | | d006 | Quality Management | | d007 | Sales | | d008 | Research | | d009 | Customer Service | +---------+--------------------+
PostgreSQLのwithを使ったupdate文
with内にupdate文を記述する場合、以下の用に記述することで「updateで更新前の値」と「updateで更新後の値」を取得することができます。
以下のSQLは、updateで更新前の値をselect文で取得します。
WITH t AS ( UPDATE products SET price = price * 1.05 RETURNING * ) SELECT * FROM products;
以下のSQLは、updateで更新後の値をselect文で取得します。
WITH t AS ( UPDATE products SET price = price * 1.05 RETURNING * ) SELECT * FROM t;
参考)PostgreSQLの公式ドキュメント:WITH問い合わせ(共通テーブル式)
まとめ
- withによる共通テーブル式をupdate文と組み合わせて使える
- OracleなどDBMSによって、withを使った記述に制約があるケースあり
- postgresqlでは、withを使ってupdate実行前の値と実行後の値を取得できる