どもです。
本日の要件は、PHPで書くなら、こう。
$tmp = サブクエリを使った重い処理;
$tmp2 = 基準値;
if($tmp == $tmp2){
echo 'A';
}else if($tmp > $tmp2){
echo 'B';
}else if($tmp < $tmp2){
echo 'C';
}
これを、MySQLで、かつプロシージャを使用せずにSELECTしたい。
冗長だったり重かったりな書き方はいくらでも思いつく、が、これをできるだけスマートに…できるのか……???
SELECT
CASE [サブクエリを使った重い処理]
WHEN 0 THEN 'A'
WHEN 1 THEN 'B'
ELSE 'C' END AS res
当然と言えば当然だが、ここまではできる。
また、WHENの数が増えても処理速度は大きく変わらなかった。つまり、WHENが増えても[サブクエリを使った重い処理]は一度しか実行されないと思われる。
SELECT
CASE WHEN [サブクエリを使った重い処理] = [基準値] THEN 'A'
WHEN [サブクエリを使った重い処理] > [基準値] THEN 'B'
ELSE 'C' AS res
これなら結果は適うが、コードが冗長なのは勿論のこと、WHENが増えるごとに明らかに実行時間が増加した。
[サブクエリを使った重い処理]はWHENの数だけ実行されてしまうのだろう。これは拙い。
ユーザー定義変数を用いれば解決できる
SELECT
@a := [サブクエリを使った重い処理] AS sub,
CASE
WHEN @a = [基準値] THEN 'A'
WHEN @a > [基準値] THEN 'B'
ELSE 'C' END AS res
こうすれば、[サブクエリを使った重い処理]の実行結果を[@a]に保存し、同一セッション内の処理で使い回せるんですって!
MySQLにも変数ってあったんだぁ…(無知)
果たして、サブクエリを使うのが正解なのだろうか?
問題は無事に解決できたのですが、残った疑問が。
そもそも、1つのSQLで解決しようとするのが正しいのか?という。
例えば、素直に[サブクエリを使った重い処理]と[基準値]をそれぞれSELECTして、実際に処理するソースの方で比較するという無難of無難な解決法があります。
あるいは、サブクエリなんて使わずに、[サブクエリを使った重い処理]を単体実行して結果を一時テーブルに落としてからSELECTするのも良いでしょう。
SQLの話ですが複雑なSQL中のサブクエリがボトルネック化したという話、そもそもMySQL5.5以前のサブクエリはとても重いという話(今どき5.5以前を使っている環境があるのか、はさて置き)などもあったりして、サブクエリを使う、という選択肢自体が安定しているとは言い難い。
一方、一時テーブルはそういった悩みからは解放されますが、例えばWHEREが複雑だった場合、SQL実行を2回に分けるのは如何なものか、という感覚がある。
いくらか調べてみましたが、この問題についてはハッキリと「どちらの方が良い」という結論は得られませんでした。
今回のケースだと、サブクエリのキーが重複する、同じ処理が2回走ってしまう可能性が高いため、一時テーブルの方がスマートに処理できると判断。
一周回ってそもそもサブクエリを使わないオチになってしまった。なんともはや。(´・ω・`)