ORDER BY句におけるNULLの同値性

アホすぎてSQLにおけるNULLの扱いが???ってなったので書く。

NULLと非NULLの比較

以下のテーブル Numbers があるとき。

A B
1 200
3 100
NULL 300
2 400

以下のSQLを実行する。

SELECT * FROM Numbers
ORDER BY A ASC;

すると、以下のいずれかの結果が取得できる。

A B
1 200
2 400
3 100
NULL 300
A B
NULL 300
1 200
2 400
3 100

NULLを非NULL値より大きいとするか、小さいとするかは、DBMS実装依存*1
この問題に対処するため、SQL-99で NULLS (FIRST|LAST) が追加された。

これはまあ常識として、ここからが本題。

NULLとNULLの比較

上記テーブルに A = NULL な行を追加。

A B
1 200
3 100
NULL 300
2 400
NULL 500

以下のSQLを実行する。

SELECT * FROM Numbers
ORDER BY A, B ASC NULLS LAST;

結果は以下になる。

A B
1 200
2 400
3 100
NULL 300
NULL 500

問題はここで、A = NULL の2行の順序は保証されるか?
処理の流れとしては、「A列でソート→A列が等しい行をB列でソート」なので、順序が保証されるための前提条件は「2つの行のA列が等しい」。

一般にNULLとNULL及び非NULL値との比較は成り立たず、SQLにおいても SELECT NULL = NULL の結果は当然FALSEになる*2

なので、保証されないように思えるが、結論としては保証される。なんでか。

SQL-99において以下が定められているため。

A NULL is “equal” to a NULL for sorting purposes.

「ソートのときは同じってことでよろ」。ずるい。

ソート以外

ORDER BYだけでなく、GROUP BYやDISTINCTについてもNULLとNULLは等しいものとして扱われる。

GROUP BY NULLってやったらNULLでまとめてくれるのはなんとなく経験上で知ってるけど、よくよく考えたらなんで?ってなった。だってNULLですよ。標準でそう定義されてるからってやつ。「集約対同値性」問題と呼ばれたりするらしい。

ここまでも、まあ、常識。ここからがいよいよ本題。

本題

このところ、乾燥肌がつらい。
もともと乾燥肌というわけではなく、実際に昨年まではまったく問題がなかった。今季からつらい。加齢によるものと思われる。

ニベアクリームはすごい。

乾燥肌業界において、品質、価格、ブランディング等、比肩する商品が見当たらない。

だが、油分がすごい。
油分は肌から水分が蒸発するのを抑え、保湿効果を高める効能がある。一方で油分が多すぎる状態はアクネ菌の増殖を促し、ニキビを作る原因になってしまう。

参考

*1:PostgreSQLDB2Oracleは前者、SQL ServerMySQLは後者

*2:NULL is NULLはTRUE