ここはとある街の総合病院。
ここには通常の診療科のほかに、
何軒もの病院をたらいまわしにされた、
それがSQL緊急救命室、
そう、

ロバート
救命室部長。腕の立つエンジニアだが、(AM9:00 病院の入り口。ワイリーが扉の前をウロウロしている)
(ロバートが出勤し、
なんだ、
あっ先生……! 違いますよ。専門課程の合格通知、
倒れたらすぐに献体として使ってやる。それがお前の医学にできる最大の貢献だ。
ふう……まったく。いい加減そのぐらいにして、
更新における冗長なサブクエリ
(治療室。ヘレンがいる。)
遅いじゃないの。先にはじめてたわよ。
すまんな。こいつが道草を食っていたせいでな。……まったく。上の空だな。早く気持ちを切り替えろ。
すいません。どうも不安で。
ああ、
はい。
カルテ1:受発注システムで利用するテーブル



代入式への行式の拡張
患者のコード
UPDATE OrderDetails
SET item = (SELECT item
FROM EntryDetails AS ED
WHERE OrderDetails.entry_id = ED.entry_id
AND OrderDetails.entry_seq = ED.entry_seq),
quantity = (SELECT quantity
FROM EntryDetails AS ED
WHERE OrderDetails.entry_id = ED.entry_id
AND OrderDetails.entry_seq = ED.entry_seq);
間違いじゃないわ。結果は正しく更新されるわ。
ふむ。とすると問題は……。
これまでの治療で見てきた症状を挙げていってみなさい。
ええっと、
このサブクエリは明らかに冗長だな。まあ、
でもどうやって直すんですか?
前回勉強したでしょう。行式よ
UPDATE OrderDetails
SET (item, quantity)
= (SELECT item, quantity
FROM EntryDetails ED
WHERE OrderDetails.entry_id = ED.entry_id
AND OrderDetails.entry_seq = ED.entry_seq);
へえ! UPDATE文のSET句でも行式は使えるのですね。
逆に聞くけど、
いやそれは……。そんないじめないでくださいよ、
気色悪い……。
シンプルさは常に良い
ヘレンが示したとおり、
その意味で、
--------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 6 | 126 | 3 (0)| 00:00:01 | | 1 | UPDATE | ORDERDETAILS | | | | | | 2 | TABLE ACCESS FULL | ORDERDETAILS | 6 | 126 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS BY INDEX ROWID| ENTRYDETAILS | 1 | 12 | 1 (0)| 00:00:01 | |* 4 | INDEX UNIQUE SCAN | SYS_C004301 | 1 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| ENTRYDETAILS | 1 | 14 | 1 (0)| 00:00:01 | |* 6 | INDEX UNIQUE SCAN | SYS_C004301 | 1 | | 0 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("ED"."ENTRY_ID"=:B1 AND "ED"."ENTRY_SEQ"=:B2) 6 - access("ED"."ENTRY_ID"=:B1 AND "ED"."ENTRY_SEQ"=:B2)
※ Oracleで取得
--------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 6 | 216 | 3 (0)| 00:00:01 | | 1 | UPDATE | ORDERDETAILS | | | | | | 2 | TABLE ACCESS FULL | ORDERDETAILS | 6 | 216 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS BY INDEX ROWID| ENTRYDETAILS | 1 | 36 | 1 (0)| 00:00:01 | |* 4 | INDEX UNIQUE SCAN | SYS_C004301 | 1 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("ED"."ENTRY_ID"=:B1 AND "ED"."ENTRY_SEQ"=:B2)
※ Oracleで取得
患者の解は、
なお、
残念なお知らせ
この便利な行式ですが、
SQL での更新は、
SQL がもともと更新機能をあまり重視していなかったことが影響しているのだろうな。ワイリー、
知りません。
聞いたワシがバカだった……。
Structured Query Language、
なるほど。もともとがSQLはSELECT文中心の言語だったわけですね。でもSQLってけっこう、
うむ、
検索SQL においては、
というと?
SET句の役割を勘違いすることに起因するケースだ。次の患者を見てみよう。
カルテ2:先ほどと同様に



SET句は更新対象を制限しない
これ、
更新結果の正しさだけを求めるなら、
カルテ1と同様に解くと、
えっと……
ブー。答えは6行。つまり全レコードよ。
このケースにおいては、
WHERE句で更新対象を制限する
UPDATEでもSELECTでも、
UPDATE OrderDetails
SET (item, quantity)
= (SELECT item, quantity
FROM EntryDetails ED
WHERE OrderDetails.entry_id = ED.entry_id
AND OrderDetails.entry_seq = ED.entry_seq)
WHERE EXISTS (SELECT *
FROM EntryDetails ED
WHERE OrderDetails.entry_id = ED.entry_id
AND OrderDetails.entry_seq = ED.entry_seq);
まったく同じ条件を、
同じに見えるのは見た目上だけで、
そっか。ところで更新対象のレコードを制限する理由は、
そうだ。今、
最後に、
そこまで言うとはものものしいですね。
まあ見ればわかる。
カルテ3:ホテルの客室を管理するテーブル

ちょっとパズル的な問題ですね
主キーがないなど、
SET句でウィンドウ関数を使えるか?
ということはウィンドウ関数を使うんですよね。よーし、
UPDATE Hotel
SET room_nbr = (floor_nbr * 100)
+ ROW_NUMBER() OVER (PARTITION BY floor_nbr);
いいな。
いいわね。
……またまた、
いや、
合ってるわね。
え……!? え……! 本当ですか。いやっほう。今日は何だかツイている気がしてきましたよ。
まあ、
そ、
SET句でのウィンドウ関数の威力
珍しくワイリーが一発で正答していますが、こんな単純なコードで可能なのか、ということに驚いた方も多いでしょう。ウィンドウ関数のおさらいになりますが、
ウィンドウ関数にORDER BYがないことが気になった人がいるかもしれません。しかし、
残念なお知らせ
またか、
- ① ウィンドウ関数でORDER BY句を指定しないことが許されない
- ② SET句に直接ウィンドウ関数を記述できない
①の問題は些細
つまり結論として、
SET句でウィンドウ関数を使う条件
ワイリーの解は、
結局ぬかよろこびですよ。
そんな卑下することはないわ。これはむしろ実装の側に責任があるのだから。
SQLがいかに更新機能をないがしろにしているかがよくわかるだろう。さて、

つまり、
そうか、
UPDATE Hotel_2
SET room_nbr
= (SELECT nbr
FROM (SELECT room_nbr,
(floor_nbr * 100) +
ROW_NUMBER() OVER(PARTITION BY floor_nbr
ORDER BY room_nbr) AS nbr
FROM Hotel_2) TMP
WHERE Hotel_2.room_nbr = TMP.nbr);
ほう、
えへへ。ここで1年間しごかれたのは無駄じゃなかったですね。
- レコードの一意識別子としては、
OracleのROWID、 PostgreSQLのOIDのように、 テーブルが保持している擬似ID列を利用する方法もあります。しかしこれは実装依存になるため、 本稿では取り上げません。
(PM5:00 休憩室。仕事を終えた3人がくつろいでいる)
いやー今日もいい仕事しましたねー。
今日
細かいところにこだわらないでくださいよ。いいじゃないですか。
あら、
え、
おめでとう、
やれやれ、
へへへ、
いいわねえ。もちろんロバートのおごりでね。
わかった、
ひゃっほう。
(1年後、……先生、
うーんむにゃむにゃ。
先生、
うーん、
夜中の3時です。
5時に起こしてって頼んだでしょ。
急患なんです。
そう……そんな急がなくっても死にはしないって……。よっこらしょと。ロバート先生とヘレン先生は?
別の患者さんを見ています。人手が足りないんです。
そっか……よし、
ええっ。ぼ、
つべこべ言うな。どうせすぐに1人でやらなきゃならないんだ。そら、
【参考資料】