2011-02-09 9 views
0

ユーザー残高のゲームポイントを更新するストアドプロシージャがあります。 5つのサブクエリを持つ挿入物です。サブクエリの1つをクエリとして分離し、バッチ全体を遅くしました。それがなければ、ストアドプロシージャは2秒以内に実行されます。それで8秒かかるでしょう。 8秒は世界の終わりではありませんが、スケーラビリティのために、私はそれをより速く完了させる必要があります。ここで分離されたサブクエリは、次のとおりです。SQL結合を改善するヘルプ

私はさらに隔離するためにやっていること
(SELECT IsNull(Sum(A.TransAmount) + Sum(Case When A.BetResult = 1 Then (A.BetWinAmount + (A.TransAmount * -1)) End), 0) 
      FROM User_T A 
      LEFT OUTER JOIN User_TD B on A.TID = B.TID 
      LEFT OUTER JOIN Lines_BL C ON B.LID = C.LID 
      LEFT OUTER JOIN Lines_BM D ON C.BMID = D.BMID 
      LEFT OUTER JOIN Event_M E ON D.EID = E.EID 
      LEFT OUTER JOIN Event_KB F ON A.TransReason = F.BID 
      LEFT OUTER JOIN Event_M G ON F.BID = G.EID 
     where A.UserID = U.UserID AND (A.IsSettled = 1) 
     AND 
     (
     (A.TransReason = 1 AND (datediff(dd, Convert(datetime, E.EDate, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo)) OR 
     (A.TransReason >= 3000 AND (datediff(dd, Convert(datetime, G.EDate, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo) 
       AND [dbo].[Event_CEAFKBID](A.TransReason) = 1) OR 
     (A.TransReason BETWEEN 3 and 150 AND (datediff(dd, Convert(datetime, A.TransDT, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo)) 
     ) 

:私はちょうどインクルードは、(where句なし)参加する上での選択*を実行すると、非常に優れた性能 - の下で> 100000行秒。私がWhere句を追加するとき、大きな減速は 'or'節や評価される必要がある関数から来ていると私は信じています。

私が理解しているように、where句の中の関数は、行を評価します。何らかの形で関数の定義をキャッシュし、その方法を評価するのとは対照的です。私はテーブルのインデックスを持っていますが、それらのいくつかが正しくないかどうか疑問に思っています。

完全なデータベース構造を理解していないと、問題がどこにあるのか把握することは非常に難しいと思いますが、さらに分離し始める方向に指摘したいと思います。

+3

クエリの実行計画を見ましたか?それはしばしばあなたを正しい方向に向けるのに役立ちます。特に、インディーズが期待された場所で使用されない場合や、最もコストがかかるものがある場合は –

+0

はい、それは機能です...何が消毒ですか? – IMAbev

答えて

2

パフォーマンスのヒットが相関サブクエリ(U.UserIdの背後にあるもの)と組み込み関数呼び出しdbo.Event_CEAFKBIDからのものであると思われます。もちろん、テーブルの大きさ(読み込まれている行の数)に依存します。これらの日時変換はすべて、非常に強力な「悪いデザイン」の匂いを生み出しませんが、パフォーマンスにあまり影響を与えないとは思いません。

これらの左外部結合オプティマイザは行のためにそれらすべてをチェックするために持っているとして、醜いです - ので、「A」が大きい場合、はすべてインクルードは全くあります場合でも、すべての行が実行されなければならないに参加しませんそこにデータ。内部結合で置き換えることができれば、そうするが、私は "テーブルE またはテーブルG"ロジックのために推測していない。 Lesses、それはあなたが持っているもののように見えるでしょう。 3つに分割して一緒に結合すると、以下のようなFrankensteinのクエリのようになります。私はこれが速く実行されるかどうか分かりません(ちょっと、私はクエリをデバッグすることはできませんし、panethesesのバランスを確認する)が、あなたは非常に速く実行する必要がありますあなたのロジックに相対的に疎なデータを持っている場合。 (私はコードを読みやすくするために日付変換を行いました。コードを読みやすくする必要があります)

SELECT isnull(sum(Total), 0) FinalTotal from (
SELECT 
    sum(A.TransAmount + Case When A.BetResult = 1 Then A.BetWinAmount - A.TransAmount else 0 End) Total 
FROM User_T A    
INNER JOIN User_TD B on A.TID = B.TID    
INNER JOIN Lines_BL C ON B.LID = C.LID    
INNER JOIN Lines_BM D ON C.BMID = D.BMID    
INNER JOIN Event_M E ON D.EID = E.EID    
where A.UserID = U.UserID 
    AND A.IsSettled = 1 
    AND A.TransReason = 1 
    AND (datediff(dd, E.EDate, @EndDate) = @DaysAgo)) 

UNION ALL SELECT 
    sum(A.TransAmount + Case When A.BetResult = 1 Then A.BetWinAmount - A.TransAmount else 0 End) Total 
FROM User_T A    
INNER JOIN Event_KB F ON A.TransReason = F.BID    
INNER JOIN Event_M G ON F.BID = G.EID   
where A.UserID = U.UserID 
    AND A.IsSettled = 1 
    AND A.TransReason >= 3000 
    AND (datediff(dd, G.EDate, @EndDate) = @DaysAgo)     
    AND [dbo].[Event_CEAFKBID](A.TransReason) = 1 

UNION ALL SELECT 
    sum(A.TransAmount + Case When A.BetResult = 1 Then A.BetWinAmount - A.TransAmount else 0 End) Total 
FROM User_T A    
where A.UserID = U.UserID 
    AND A.IsSettled = 1 
    AND A.TransReason BETWEEN 3 and 150 
    AND datediff(dd, A.TransDT, @EndDate) = @DaysAgo) 
) ThreeWayUnion 
+0

それはすごかった、フィリップ!思考のための多くの食糧。私はこれを実行し、何が起こるかを見ようとします。 – IMAbev

+0

多くのテストを行います。 UNIONとUNION ALLの重要な違いに注意してください.1つではなく3つの相関サブクエリを実行するので、遅くなる可能性があります。 –

+0

Phil(または誰でも)私はdatetimeの代替案について再読していますコンバージョン行が2番目に発生したときに行を書いている場合、行が特定の日付になったときにどのように判断するのでしょうか? – IMAbev

1

ケースはwhereの原因に置くことができます。最初の行を直接選択することはできません。 なぜあなたはこのステートメントであなただけのテーブルA、E、Gを使用する場合は、多くの参加を置く必要がありますか?

パフォーマンスが向上するように、管理スタジオで実行プランを使用できます。

+0

テーブルA、E、Gに到達する唯一の方法は、結合の他のテーブルを調べることです。私は実行計画を使用しましたが、それを解読することは別の質問になるでしょう。 – IMAbev

1

相関サブクエリは、クエリでカーソルを使用することに等しい非常に貧弱なプログラミング手法です。代わりにそれを派生テーブルにします。

そして、これらの機能は、あなたを遅くしています。 datetimeに変換する必要がある場合は、データベース構造を固定し、データをdatetimeとして正しく格納する必要があります。

+0

素晴らしい情報、ありがとう!私は導出されたテーブルを見て、私は同様にクリーンアップすることができる多くの他のストアドプロシージャを持っていると確信しています。 – IMAbev

+0

派生テーブルの代わりにCTEを使用することもできます。 – HLGEM

1

DATEDIFF関数の日時の変換を行う必要がありますか?あなたはテストとして日付を格納していますか、時間を取り除くために再変換していますか?あなたがいるならば、時間を含めて異なる日が異なるようにする必要はありません。

+0

偉大な質問 - 本当に私は考えている...私は時間を取り除くためにreconvertingしていた。起こるさまざまなイベント(挿入された行)のために、実際のタイムスタンプがありますが、実際には1日全体のアクティビティが必要です。しかし、私はまだ再変換する必要があると思う。 – IMAbev

+0

上記のように、私は日付変換を削除し、パフォーマンスの向上はごくわずかでした。私のコンバージョンは、コンバーターになっているデータが既にdatetimeデータ型だったため、さらに検討すると冗長です。おそらくそれが無視できるほどのパフォーマンス向上がなかった理由でしょうか? – IMAbev

1

外部結合が必要かどうかを確認する必要があります。内部結合よりも高価です。あなたは支配的なテーブルから来たいくつかの値を持っています.Aをタグ付けしたOR条件と、Gを参照するOR条件があります。

SELECT SUM(x.result) 
    FROM (SELECT A.TransAmount + CASE WHEN A.BetResult = 1 
           THEN (A.BetWinAmount + (A.TransAmount * -1)) 
           ELSE 0 END AS result 
      FROM A 
     WHERE A.TransReason BETWEEN 3 AND 150 
      AND datediff(dd, Convert(datetime, A.TransDT, 101), 
          Convert(datetime, @EndDate, 101)) = @DaysAgo 
      AND A.UserID = U.UserID -- Where does alias U come from? 
      AND A.IsSettled = 1 
     UNION 
     SELECT A.TransAmount + CASE WHEN A.BetResult = 1 
           THEN (A.BetWinAmount + (A.TransAmount * -1)) 
           ELSE 0 END AS result 
      FROM User_T A 
      JOIN User_TD B ON A.TID = B.TID 
      JOIN Lines_BL C ON B.LID = C.LID 
      JOIN Lines_BM D ON C.BMID = D.BMID 
      JOIN Event_M E ON D.EID = E.EID 
     WHERE A.TransReason = 1 
      AND datediff(dd, Convert(datetime, E.EDate, 101), 
          Convert(datetime, @EndDate, 101)) = @DaysAgo 
      AND A.UserID = U.UserID -- Where does alias U come from? 
      AND A.IsSettled = 1 
     UNION 
     SELECT A.TransAmount + CASE WHEN A.BetResult = 1 
           THEN (A.BetWinAmount + (A.TransAmount * -1)) 
           ELSE 0 END S result 
      FROM User_T A 
      JOIN User_TD B ON A.TID = B.TID 
      JOIN Lines_BL C ON B.LID = C.LID 
      JOIN Lines_BM D ON C.BMID = D.BMID 
      JOIN Event_M E ON D.EID = E.EID 
      JOIN Event_KB F ON A.TransReason = F.BID 
      JOIN Event_M G ON F.BID = G.EID 
     WHERE A.TransReason >= 3000 
      AND datediff(dd, Convert(datetime, G.EDate, 101), 
          Convert(datetime, @EndDate, 101)) = @DaysAgo 
      AND [dbo].[Event_CEAFKBID](A.TransReason) = 1 
      AND A.UserID = U.UserID -- Where does alias U come from? 
      AND A.IsSettled = 1 
     ) AS x 

ここで考えているのは、内部結合クエリは外部結合クエリよりも速く、中間結果の合計はDBMSにとって難しいことではないことです。おそらくIFNULLの必要性も避けています。

エイリアスUはおそらく、これが一部である外部クエリへの参照です。

+0

こんにちはジョナサン。そこに素晴らしいもの! U表はユーザー表です。あなたの応答とフィリップの反応で、私は本当に連合の質問の周りに頭を浮かべる必要があるように見えます。 – IMAbev

+0

ジョナサン - ついに私は座ってこれを理解する時間がありました。あなたの例の助けを借りて、私はサブクエリのこのセクションを書き直すことができました。私は古いスクリプトと新しいスクリプトの両方をプロファイラで実行しました。私は21,000以上の読み込みと250msから1318回の読み込みと62msになりました!うまくいけば、私はこのすべてを一緒に投げるとき、私は巨大なパフォーマンスの向上を見ます! FYI日付変換を取り除くことを提案している人もいます。私は、5msの利得を提供するだけでなく、または伴わずにテストしました。 – IMAbev

関連する問題