複数のCOUNTがCASEの1つのSUMよりも速いのはなぜですか?


14

次の2つのアプローチのどちらが速いかを知りたかったのです。

1)3つCOUNT

 SELECT Approved = (SELECT COUNT(*) FROM dbo.Claims d
                  WHERE d.Status = 'Approved'),
        Valid    = (SELECT COUNT(*) FROM dbo.Claims d
                    WHERE d.Status = 'Valid'),
        Reject   = (SELECT COUNT(*) FROM dbo.Claims d
                    WHERE d.Status = 'Reject')

2)SUMFROM-clause:

SELECT  Approved = SUM(CASE WHEN Status = 'Approved' THEN 1 ELSE 0 END),
        Valid    = SUM(CASE WHEN Status = 'Valid'    THEN 1 ELSE 0 END),
        Reject   = SUM(CASE WHEN Status = 'Reject'   THEN 1 ELSE 0 END)
FROM dbo.Claims c;

違いがとても大きいことに驚いた。3つのサブクエリを含む最初のクエリはすぐに結果を返しますが、2番目のSUMアプローチでは18秒かかります。

Claims〜1800万行を含むテーブルから選択するビューです。ClaimStatusstatus-nameを含むテーブルへのFK-Columnにインデックスがあります。

なぜそれが私が使用しているかどうか、このような大きな違いを生むんCOUNTSUM

実行計画:

合計で12のステータスがあります。これらの3つのステータスは、すべての行の7%に属します。


これは実際のビューであり、関連があるかどうかはわかりません:

CREATE VIEW [dbo].[Claims]
AS
SELECT 
   mu.Marketunitname AS MarketUnit, 
   c.Countryname     AS Country, 
   gsp.Gspname       AS GSP, 
   gsp.Wcmskeynumber AS GspNumber, 
   sl.Slname         AS SL, 
   sl.Wcmskeynumber  AS SlNumber, 
   m.Modelname       AS Model, 
   m.Salesname       AS [Model-Salesname], 
   s.Claimstatusname AS [Status], 
   d.Work_order      AS [Work Order], 
   d.Ssn_number      AS IMEI, 
   d.Ssn_out, 
   Remarks, 
   d.Claimnumber     AS [Claim-Number], 
   d.Rma_number      AS [RMA-Number], 
   dbo.ToShortDateString(d.Received_Date, 1) AS [Received Date], 
   Iddata, 
   Fisl, 
   Fimodel, 
   Ficlaimstatus 
FROM Tabdata AS d 
   INNER JOIN Locsl AS sl 
           ON d.Fisl = sl.Idsl 
   INNER JOIN Locgsp AS gsp 
           ON sl.Figsp = gsp.Idgsp 
   INNER JOIN Loccountry AS c 
           ON gsp.Ficountry = c.Idcountry 
   INNER JOIN Locmarketunit AS mu 
           ON c.Fimarketunit = mu.Idmarketunit 
   INNER JOIN Modmodel AS m 
           ON d.Fimodel = m.Idmodel 
   INNER JOIN Dimclaimstatus AS s 
           ON d.Ficlaimstatus = s.Idclaimstatus 
   INNER JOIN Tdefproducttype 
           ON d.Fiproducttype = Tdefproducttype.Idproducttype 
   LEFT OUTER JOIN Tdefservicelevel 
                ON d.Fimaxservicelevel = Tdefservicelevel.Idservicelevel 
   LEFT OUTER JOIN Tdefactioncode AS ac 
                ON d.Fimaxactioncode = ac.Idactioncode 

両方のリンクCOUNTがプランのバージョンを指しているようです。SUM正しいプランを指すようにバージョンのいいねを編集できますか?
ジェフパターソン

他のstatiiの行に対するこれら3つのstatiiの行の比率はどのくらいですか
マックスヴァーノン

1
@MaxVernon:はい、もちろん、ゼロが多すぎます。そうですね。コメントを削除させてください。はい、他のステータスには1670万行があります。ほとんどがAuthorizedです。
ティムシュメルター

2
2番目の計画では、テーブル全体を12回スキャンしなければならないことに苦しんでいます(これが示していることです)。これは、おそらく述語をスキャンにプッシュダウンできないことに起因しています。バリアントに追加するWHERE c.Status = 'Approved' or c.Status = 'Valid' or c.status = 'Reject'と、パフォーマンスはどうなりますかSUM
マックスヴァーノン

@MaxVernon:合計で12のステータスがあります。それは本当に私にとっては問題ではありませんが、オプティマイザーがこれを処理できないことに非常に驚きました。実行計画の分析スキルに本当に取り組むべきです。それを答えてください。あなたの仮定は何ですか、なぜSQL-Serverは3つのステータスしかスキャンできないのですか?
ティムシュメルター

回答:


19

COUNT(*)バージョンは、単にあなたが選択されている各ステータスの後のに対し、あなたは、ステータス列に持つインデックスに求めることが可能であるSUM(...)バージョンがインデックスに12回(独自のステータスタイプの合計数)を模索する必要があります。

明らかにインデックスを3回シークすると、12回シークするよりも速くなります。

最初のプランでは238MBのメモリ許可が必要ですが、2番目のプランでは650MBのメモリ許可が必要です。それはあり、より大きなメモリ許可はすぐにはるかに遅いクエリを作成、埋めることができなかったということ。

2番目のクエリを次のように変更します。

SELECT  Approved = SUM(CASE WHEN Status = 'Approved' THEN 1 ELSE 0 END),
        Valid    = SUM(CASE WHEN Status = 'Valid'    THEN 1 ELSE 0 END),
        Reject   = SUM(CASE WHEN Status = 'Reject'   THEN 1 ELSE 0 END)
FROM dbo.Claims c
WHERE c.Status = 'Approved'
    OR c.Status = 'Valid'
    OR c.Status = 'Reject';

これにより、クエリオプティマイザーはインデックスシークの75%を排除することができ、必要なメモリ許可、I / O要件、および結果が得られるまでの時間が短縮されます。

このSUM(CASE WHEN ...)構造は、クエリオプティマイザーStatusがプランのインデックスシーク部分に述語をプッシュすることを本質的に防ぎます。


記憶力に優れています。32GBのすべてが現在使用されていることに気付きました(空き容量は300 MBのみです)。編集ただし、メモリをいくらか解放しました。結果は同じです
ティムシュメルター

max server memoryオプションを確認したい場合があります-システムの正しい値に設定する必要があります。あなたはを見てみたいことがあり、この質問とそれを行う方法の詳細については答え。
マックスヴァーノン

1
残念ながら、このサーバーはデータベースだけでなく、SSASキューブと一部のツール(イントラネットWebアプリを含む)にも使用されます。しかし、私はすでに最大12GBを割り当てています。
ティムシュメルター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.