MySQLのドキュメントによると:
原則として、ユーザー変数に値を割り当てたり、同じステートメント内で値を読み取ったりしないでください。期待どおりの結果が得られる場合がありますが、これは保証されていません。
ただし、High Perfomance MySQLの本には、この戦術を使用してクエリのパフォーマンスを向上させる例がいくつかあります。
次はアンチパターンですか?もしそうなら、良いパフォーマンスを維持しながらクエリを書くためのより良い方法はありますか?
set @last = null;
select tick, count-@last as delta, @last:=count from measurement;
明確にするために、私の目標は、この行と最後の行の違いを見つけることです。私のテーブルには、日時列であるティックの主キーがあります。
アップデート:
Shlomiの提案を試した後、元のクエリに戻りました。集計関数でcaseステートメントを使用すると、予期しない動作が発生することがわかりました。たとえば、次を参照してください。
case when (@delta := (max(measurement.count) - @lastCount)) AND 0 then null
when (@lastCount := measurement.count) AND 0 then null
else @delta end
mysqlは、結果の最初のパスで集計関数を含まない式を評価し、次に2番目の(グループ化)パスで集計式を評価するようです。その2回目のパス中またはその後のケース式を評価し、その評価で1回目のパスから事前に計算された値を使用するようです。その結果、3行目の@deltaは常に@deltaの初期値になります(グループ化パスまで割り当てが行われなかったため)。@deltaを使用してグループ関数を行に組み込みようとしましたが、期待どおりに動作させることができませんでした。したがって、最終的には、この問題が発生しなかった元のクエリに戻ります。
このようなクエリをより適切に処理する方法について、これ以上の提案を聞きたいと思います。
アップデート2:
この質問に対する回答がなかったことをお詫び申し上げます。これまで、これ以上調査する機会はありませんでした。
Shlomiのソリューションを使用すると、@last変数を読み取ったときにgroupby関数を使用していたが、設定したときは使用していなかったため、問題が発生したようです。私のコードは次のようになりました。
CASE
WHEN (@delta := count - @last) IS NULL THEN NULL
WHEN (@last:= count ) IS NULL THEN NULL
ELSE (CASE WHEN cumulative THEN @delta ELSE avg(count) END)
END AS delta
MySQLは、最初のパスで集計関数を含まない式と、2番目のパスで集計関数を含む式を処理しているように見えます。上記のコードの奇妙な点は、cumulative
真と評価された場合でも、MySQLは句AVG
内の集計関数を確認し、2番目のパスで内部式全体を評価することを決定する必要があることです。は集計関数のない式に設定されているため、最初のパスで設定されているように見え、2番目のパスが発生するまでに、MySQLは設定された行とを評価します。ELSE
CASE
@delta
@delta
@last
最終的には、最初の式にも集計関数を含めることで修正を見つけたようです。このようなもの:
CASE
WHEN (@delta := max(count) - @last) IS NULL THEN NULL
WHEN (@last:= max(count) ) IS NULL THEN NULL
ELSE (CASE WHEN cumulative THEN @delta ELSE avg(count) END)
END AS delta
MySQLが何をしているのかについての私の理解は、ソースコードを読んでいないので、純粋にテストと推測に基づいていますが、これが同様の問題に遭遇する可能性のある他の人に役立つことを願っています。
それは本当に良い解決策なので、私はShlomiの答えを受け入れるつもりです。集計関数の使用方法に注意してください。