0

MySQLのドキュメントによると:

原則として、ユーザー変数に値を割り当てたり、同じステートメント内で値を読み取ったりしないでください。期待どおりの結果が得られる場合がありますが、これは保証されていません。

http://dev.mysql.com/doc/refman/5.6/en/user-variables.html

ただし、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は設定された行とを評価します。ELSECASE@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の答えを受け入れるつもりです。集計関数の使用方法に注意してください。

4

1 に答える 1

3

私はこの問題を徹底的に調査し、上記にいくつかの改善を書きました。

この投稿では、順序が期待できる関数を使用するソリューションを提供します。去年の私の話も考えてみてください。

などの構造やなどのCASE関数は、COALESCE基本的な動作がわかっています(少なくともこれが変更されるまでは?)。

たとえば、CASE句は、WHEN定義の順序で条件を1つずつ検査します。

元のクエリの書き直しを検討してください。

select 
  tick,
  CASE
    WHEN (@delta := count-@last) IS NULL THEN NULL
    WHEN (@last:=count ) IS NULL THEN NULL
    ELSE @delta
  END AS delta
from 
  measurement,
  (select @last := 0) s_init
;

このCASE条項には3つのWHEN条件があります。成功した最初のものに出会うまで、順番に実行します。最初の2つは常に失敗するように作成しました。したがって、最初に実行し、次に2番目を実行し、最後に3番目を返します。常に

したがって、評価の順序を期待するという問題を克服します。これは、、、などGROUP BYのより複雑な句を追加し始めると、ほとんどの場合明らかな、実際の真の問題です。DISTINCTORDER BY

最後に、私のソリューションは、結果セットの最初の行のあなたのソリューションとは異なります。あなたのソリューションでは、が返され、私のソリューションでは、とNULLの間のデルタが返されます。もし私が使っていたら、他の方法で条件を変更する必要があったでしょう-それらが値で失敗することを確認してください。0countNULLWHENNULL

于 2012-07-21T05:06:43.297 に答える