ど素人から毛を生やす。<延>

[MySQL]サブクエリの実行結果をCASEで比較したい話。(ユーザ定義変数)

Web > Other 2022年10月14日(最終更新:2年前)

2022年10月14日に作成されたページです。
情報が古かったり、僕が今以上のど素人だった頃の記事だったりする可能性があります。

どもです。
本日の要件は、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にも変数ってあったんだぁ…(無知)

参照:MySQL 5.6 リファレンスマニュアル

果たして、サブクエリを使うのが正解なのだろうか?

問題は無事に解決できたのですが、残った疑問が。
そもそも、1つのSQLで解決しようとするのが正しいのか?という。

例えば、素直に[サブクエリを使った重い処理]と[基準値]をそれぞれSELECTして、実際に処理するソースの方で比較するという無難of無難な解決法があります。

あるいは、サブクエリなんて使わずに、[サブクエリを使った重い処理]を単体実行して結果を一時テーブルに落としてからSELECTするのも良いでしょう。

SQLの話ですが複雑なSQL中のサブクエリがボトルネック化したという話、そもそもMySQL5.5以前のサブクエリはとても重いという話(今どき5.5以前を使っている環境があるのか、はさて置き)などもあったりして、サブクエリを使う、という選択肢自体が安定しているとは言い難い。

一方、一時テーブルはそういった悩みからは解放されますが、例えばWHEREが複雑だった場合、SQL実行を2回に分けるのは如何なものか、という感覚がある。

いくらか調べてみましたが、この問題についてはハッキリと「どちらの方が良い」という結論は得られませんでした。
今回のケースだと、サブクエリのキーが重複する、同じ処理が2回走ってしまう可能性が高いため、一時テーブルの方がスマートに処理できると判断。

一周回ってそもそもサブクエリを使わないオチになってしまった。なんともはや。(´・ω・`)

この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)