Techioz Blog

Sequel で複雑な case ステートメントを作成しますか?

概要

MySQL で動作するかなり複雑な case ステートメントがあります。

SELECT # rest of code omitted
CASE WHEN code = 'a' THEN 'x'
  WHEN code IN ('m', 'n') THEN 'y'
  WHEN class IN ('p', 'q') AND amount < 0 THEN 'z'
  ELSE NULL END AS status
FROM # rest of code omitted

ただし、これを Sequel で書こうとする試みはすべて失敗しました。私はこれをテンプレートとして使用しています:

Sequel.case([[:c, 1], [:d, 2]], 0) # (CASE WHEN "c" THEN 1 WHEN "d" THEN 2 ELSE 0 END)

(Jeremy Evans の Github より)

私の最善の推測は次のとおりです。

dataset.select( # rest of code omitted...
[[(:code => 'a'), 'x'],
[(:code => 'b'), 'y'],
[(:class => ['p', 'q'], :amount < 0), 'z']].case(nil).as(:status))

何か案は?

解決策

これを試してみた結果、続編の gem は「シンプル、柔軟、そして強力」であることを目指しているものの、少し複雑になると構文がかなり複雑になるという結論に達しました。

あなたのクエリに対する私の最善の試みは次のとおりです。

DB[:testtable].select( 
  Sequel.case([
  [{code: 'a'}, 'x'],
  [{code: ['m', 'n']}, 'y'], 
  [{class: ['p', 'q'], (Sequel.expr(:amount) > 0) => true}, 'z']], 
  nil).
  as(:status)
)

これにより、次の (ほぼ正しい) SQL が生成されます。

SELECT (
CASE WHEN (`code` = 'a') THEN 'x' 
WHEN (`code` IN ('m', 'n')) THEN 'y' 
WHEN ((`class` IN ('p', 'q')) AND ((`amount` > 0) IS TRUE)) THEN 'z' 
ELSE NULL END) AS `status` FROM `testtable`

case ステートメント内で不等号演算子を使用する方法がわかりませんでした。もしかしたら、そのほうが幸運に恵まれるかもしれません。

私の提案は、クエリを SQL で書くだけで、非常に読みやすく、保守しやすくなるということです。