MySQLのgenerated columnを使ってOR検索を潰す
製品に販売期間設定がある場合に、販売可能なものを探すのに↓のようなクエリが発行されるとする
(valid_from と valid_to が null の場合は、期間の設定が特に設定されておらず期限のチェックが不要という意味)
SELECT * FROM products WHERE (valid_from < '2018-12-26' OR valid_from IS NULL) AND (valid_to > '2018-12-26' OR valid_to IS NULL)
このとき valid_from と valid_to の複合インデックスがあっても、or検索があるため上手く使ってもらえない(と思う)
このorを消す方法を考える
valid_from、valid_to に NOT NULL制約をつけそれぞれに日付の最小値、最大値を入れることができればこの問題は解決できるが、既存のテーブルに値を追加していくのはDBでの対応以外にもアプリケーション側での対応も必要になる場合があり、すぐに対応ができないことがある。
そこで今回はgenerated columnを使う方法を試みる。
ALTER TABLE products ADD filled_valid_from datetime AS IFNULL(valid_from, '1000-01-01 00:00:00') ALTER TABLE products ADD filled_valid_to datetime AS IFNULL(valid_to, '9999-12-31 23:59:59')
↑のクエリを投げると、AS
移行の計算結果が filled_hogehoge の中に入ることになる。これによりNULLがない日付型のカラムが利用できることになる。
SELECT * FROM products WHERE filled_valid_from < '2018-12-26' AND filled_valid_to > '2018-12-26'
というわけで↑のように OR 〜 IS NULL
を消すことができた。
あとは↓のようにインデックスを貼ればよい。
ALTER TABLE products ADD INDEX idx_filled_valid_dates(filled_valid_from, filled_valid_to)
(ただこの例だと filled_valid_to
が使われることはない?かもしれない)
やはりnull許容のカラムは悪い文明!!
粉砕する!!!
まったく別件バウアーなんですけど、OR検索を潰すエントリの販売可能なものを探すクエリの不等号とかおかしくないですか??
— Ryuta Kamizono (@kamipo) 2019年1月23日
この方法でNULL排除してもこの不等号の向きを維持してORなくせないと思うんですが…!
初稿時、不等号の向きとかがおかしかったので修正しました…