00:00
はい、こんにちは、ヨシオリです。今日はですね、SQLのNullについて話そうかなと思っていますっていうのも何かというと、社内でその話をして
ちょっとSQLのNull分かりにくいからドキュメント確認って書いてたので、今、脳内にそれがキャッシュして残ってるんで、その話をしようかなと思います。
それで、SQLってNullの扱いが難しくてっていう話をすると、Nullってどの言語でも難しいじゃんっていう話になるんですけども
まあまあまあ、それは置いといて、SQLはNullの扱いが難しいんですよね。
NullっていうのがNullっていう値ではなくて、値が不定、未確定であるっていうことを表しているのがNullなんですよね。
なんで、SQL書いたことある人は、みんな1回ぐらいWearhackでNullと比較するときにね、
="null="って書いて、="null="だと比較できないみたいなのをみんな経験してisNullって書くっていうのを覚えたと思うんですけども
何かっていうと、未確定なもの="未確定="は、その未確定っていう言葉で言うと一緒なんだけれども
値としては未確定だから違うもの扱いになっちゃうんですよね。
それはAになるかもしれないし、Bになるかもしれないものを比較しているので
絶対に="null="でトゥルーになることはないんですよ、Nullと比較をして。
っていうのが、散地理論とかもいろいろあるんですけども
SQLとかでの未確定であるNullの扱いですね。
確かJavaScriptのUndefinedとかも似たような扱いの難しさを持っていたと思うんですけど
だからJavaScriptはNullとUndefinedを両方持ってるんですよね。
それがそれでまたすごい色々ややこしくなってると思うんですけども
それはとりあえず置いといてで
SQLのNullは未確定を表してますよって言うんだけども
じゃあ実際どういう挙動をするかっていうと
イコールとかですね、大なり小なりとかノットイコールとかで比較をすると
Nullが返ってくるんですよね。
これはセレクト1イコールNullとかって書いてもらうと
すぐ結果としてNullが返ってきてるように見れると思うのでわかると思うんですけども
SQLの標準の仕様でNullは何かと比較をするとNullが返ってくるっていうのが
標準、SQL標準になってるんですよ。
SQLスタンダードにそうなっていて
またちょっと脱線するんですけど
SQLスタンダードになっていてって言うんですけども
これ俺は読んだことがなくて
っていうか多分世の中にそんな読んだことがある人いっぱいはいないだろうなと思っていて
何でかっていうとSQLスタンダードって有料じゃないと読めないんですよ、この標準化ドキュメント
クソだなと思うんですけども
何でこう伝聞でSQL標準はそうなってるよっていうのを聞いたことしかないんですが
まあまあいろんなとこで言ってるし
ポストグレースのドキュメントとかにもそう書いてあるんで
まあまあ正しいでしょう。
まあまあという話は置いといて
何でNullって他と比較するとNullになるんですね。
で、Nullが返ってきたんですよ。
で、NullってWellアークとかでは基本的にはフォルス扱いになるので
一致してない扱いになってしまうものなんですね。
で、こっからがさらにややこしいんですけども
True and NullってやるとNullになるんですよ。
要は条件式Wellアークで複数条件を書くときって
03:00
&で繋げるじゃない、&とorで繋げるじゃないですか。
で、&で繋げたときに
True and NullはNullになるんですね。
で、False and NullはFalseになるんですね。
で、True or NullだとTrueが返るんですね。
で、False or NullだとNullが返るんですね。
で、False or NullだとNullが返るんですよ。
っていうのが出てくるんですね。
で、これは別に冷静に考えると難しくないんですよ。
その比較演算子である&とorですね。
&とorの条件の演算子ですね。
で、&ってほとんどのプログラミング言語の挙動が全部そうだと思うんですけども
左辺を評価して
Trueだった場合
真偽値でいうと偽に値する値だった場合に
右辺を評価するっていうのが&の基本的な挙動なんですね。
なので左辺をまず評価します。
で、それがTrueに値する
真偽値の真であったら場合に
右辺を評価するっていうのが&の挙動なんで
左辺がNullだったら右辺は評価しないし
左辺がTrueだったら右辺を評価してNullが返るという
当たり前の挙動なんですね。
で、orは逆ですね。
左辺を評価してFalseの値になったときに右辺を評価する。
左辺を評価してTrueの値になるときは
もう右辺は評価せずに
左辺をそのまま返すっていうのが
orの挙動なので
冷静に考えれば難しくはないんですが
さっき言っていた何かの値とNull
1イコールNullって比較するとNullが返ってくるんですよ。
何かと比較をしたときにNullが返ってきて
しかも&で繋げると全部Nullになってしまう可能性がある
みたいなのがSQLでのNullの扱いを
むちゃくちゃ難しくしていて
ただでさえこのWayアークでさえ難しくなってくるので
SQLというかデータベースの中での制約とかにも使われるんですよね
そのユニーク制約とかでも
この条件式が一致するので
例えばAっていうカラムがNullが入るAっていうカラムに
ユニーク制限をかけたとするじゃないですか
Aはユニーク制限がかかってるカラムですよ
でもNullが入りますってなると
標準ではNullがガンガンガンガン何個でも入れられるんですよ
なぜならNullは絶対にNullとイコールにならないんで
っていう挙動があったりとか
まだ一個ぐらいだったらそんな難しくないと思うんですけど
これが複合カラムになるとややこしく
複合ユニークになるとややこしくて
ややこしくてですね
ABCってカラムがあってCだけNullableで
そのAとBの値が一緒だったときに
CがNullだった場合
AとBの値が同じものを大量にぶち込めるんですよ
Aが例えば数字で1
Bがテストって書いてあって
CをNullってやったら
この1テストNullっていうデータを
無限に入れられるんですね
そのABCを複合ユニークで設定していても
っていうややこしい仕様とかが
全然生まれてるんで
06:00
データベースでカラムを定義するときは
なるべくNotNullにした方がいいよねみたいな
ドキュメントを今日書いたんです
頭にNullのことがいっぱい入ってるので
今日は話してみます
ちなみにPostgresは15から
ユニーク制限を作るときに
Nullをディスティンクとして
NullはNullとして扱うみたいな扱いが
オプションとして追加されたんで
それを使うと
さっき言ってたABCのCだけNullableで
Nullが入ってるときみたいなのは
防げるんですが
これはこれでSQL標準の動きと
違う動きになるんで
また別の混乱を呼ぶんじゃないかな
みたいなのを俺は思っていて
あんま近寄らんでおこう
っていう風に思っています
とりあえずNullableなカラムはやめようね
っていう話ですね
じゃあNullableなカラムはやめて
どうするかっていうと
空であることを表したいんだったら
一個は空を表す値を入れるってことですよね
Emptyを表すための値を入れる
Intとかであると
よくやるのは-1とかを入れるとかですかね
Enumとかであれば
Emptyであるものを
定義してそこに入れるようにするとか
あとUUIDとかは
UUID側の仕様でですね
00000っていう
全部ゼロのUUIDは
NilUUIDっていう定数として定義されていて
これは値が決まっていない
Nullからのことを表したいときは
これを使ってくださいみたいなのが
それはRFCでちゃんと定義されているので
それを使ったりとかするといいかもしれないですね
はいっていう感じで
なんでデータベースのカラムは
なるべく乗っ取るようにしましょうね
っていう話でした
じゃあ