UPDATE 文中で使える LIMIT 相当構文を DBMS 毎に見てみよう
Oracle
Oracle Technology Network (OTN)より。
create table test1 (id number);
I have put 20 different numbers into the table and now I
want to update the last 4 of them.
See below:update (select id from test1 where id in (select id from (select id from test1 order by id desc) where rownum < 5)) set id = 100;
I have just ordered rows by descending manner, selected
last 4 and when have used IN operand to restrict TEST1 selection. Then it's easy to use subquery-update statement.
ROWNUM*1 を使った副問合せで、任意の件数を抽出し、その結果*2に対して更新をかけている。
しかし、これだけのことに、
UPDATE (SELECT id FROM test1 WHERE id in (SELECT id FROM (SELECT id FROM test1 ORDER BY id DESC) WHERE ROWNUM < 5) ) SET id = 100;
こんな仰々しい SQL を書いちゃうのもナンだかなァというところである。
Oracle であれば、PL/SQL 無名ブロックを用いて割と好きに書ける*3ので、よい子は素直にカーソルループで書く。
declare cursor cr as SELECT id_name,amount FROM ( SELECT id,name,amount FROM my_table ORDER BY amount ) WHERE ROWNUM < 5 FOR UPDATE; begin for pnt in test loop UPDATE ... SET ... WHERE CURRENT OF cr; end loop; end;
DB2
UPDATE (SELECT SALARY,
4 COMM,
4 AVG(SALARY) OVER (PARTITION BY WORKDEPT),
4 AVG(COMM) OVER (PARTITION BY WORKDEPT)
4 FROM EMPLOYEE)
4 AS E(SALARY, COMM, AVGSAL, AVGCOMM)
4 SET (SALARY, COMM)
4 = (AVGSAL, AVGCOMM)
4 WHERE EU.EMPNO = '000120'上のステートメントは、意味的には次のステートメントと同等ですが、 4EMPLOYEE 表へのアクセスを一度しか必要としません。それに対し、次のステートメントでは、 4EMPLOYEE 表を二度指定します。
UPDATE EMPLOYEE EU
SET (EU.SALARY, EU.COMM)
=
(SELECT AVG(ES.SALARY), AVG(ES.COMM)
FROM EMPLOYEE ES
WHERE ES.WORKDEPT = EU.WORKDEPT)
WHERE EU.EMPNO = '000120'
また、Oracle における ROWNUM 相当の構文は、知っておきたいデータベース移行術「Oracle→DB2編」によると、こんな感じ。
Oracle
SELECT * FROM emp WHERE ROWNUM<11;DB2
SELECT * FROM emp FETCH FIRST 10 ROWS ONLY;
MySQL
MySQL は、素直に LIMIT が UPDATE 文の中で使える。あの「他のDB」とは、もしや MySQL だった*4のか?
MySQL バージョン 3.23 以降では、LIMIT row_count を使用して UPDATE のスコープを制限することができます。
PostgreSQL
FAQ を見る限りでは SELECT で LIMIT を使えるので、これを副問合せと組み合わせ UPDATE できるはず……だが、実際やるまで何とも言えない*5。
*1:ROWNUM は、結果セットに付加される連番を表す擬似列。引用の SQL では ORDER BY 及び ROWNUM 指定に対して各々1回ずつ副問合せを行っているが、これは、1度の問合せに両方のキーワードを使うと、結果セットに ROWNUM を振った後にORDER BY 指定によってソートされてしまうから(ROWNUM の順ではなくなるから)である。
*2:SELECT 文の FROM 句に来るとき、これをインラインビューと呼ぶ。
*3:冒頭で述べた日本メーカー製の RDBMS は、せっかく PL/SQL と類似の手続き型 SQL 拡張言語をもっているのに、ストアドプロシージャでしか手続き型ルーチンを書けない。
*5:例によって、冒頭で述べた日本メーカー製の DBMS では、UPDATE 〜 SELECT 構文に「SELECT するテーブルと UPDATE するテーブルが同一であってはならない」という制限がある。