🤔

MySQLのWHERE句の短絡評価

2021/09/09に公開

最近以下のサイトで遊んでいます。その中で出会ったSQLの挙動について書きます。
https://los.rubiya.kr/

WHERE句で短絡評価はされる

SELECT * FROM hoge WHERE 1=1 OR (SELECT 1 UNION SELECT 2);

上記のようなクエリを考えてみます。これは正しく実行されhogeテーブルの内容が単純にSELECTされます。

SELECT * FROM hoge WHERE 1=0 OR (SELECT 1 UNION SELECT 2);

今度はORの左辺を1=0とします。すると Query Error: Error: ER_SUBQUERY_NO_1_ROW: Subquery returns more than 1 rowとエラーが発生しました。

この2つのクエリから以下の挙動が理解できます。

  • (SELECT 1 UNION SELECT 2)は評価されるとエラーとなる
  • ORの両方は必ずしも評価されない。短絡評価されることがある。

評価は順番通りじゃない

冒頭で紹介したサイトの問題でSQLのエラーが出た場合に画面の表示が変わるのでそれを手がかりにパスワードを探索する問題がありました。そこで上記の実験をした後以下のようなクエリを試します。

SELECT * FROM Users WHERE length(pw) > 0 OR (SELECT 1 UNION SELECT 2);

するとこれはエラーとなってしまいました。
予想では左から順番に評価され、ORの左辺の条件がtrueならば短絡評価で右辺は実行されないと思いましたがどうやらそうではないようです。

クエリのオプティマイザが軽量なものから評価するようにしていると仮定すると右辺は定数をSELECTしているだけなので先に評価される気がします。そこで

SELECT * FROM Users WHERE length(pw) > 0 OR (SELECT length(id) UNION SELECT length(pw));

と書き換えてみます。すると左辺が評価され左辺がtrueの場合には短絡評価されエラーが出なくなりました!

まとめ

挙動の理由は予想までではっきりと根拠までは調べられていません。申し訳ありません。
想定通りに短絡評価されないことがあるという紹介でした。他のDBだとどういう挙動になるかなども興味がありますが今回は調べられていません。
以下に今回実験したクエリの内容をおいておきます。
https://www.db-fiddle.com/f/dKTit2o6wSZUENwtK4sWdy/2

Discussion