次の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)SUM
- clauseを使用したFROM
:
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
は、約1,800万行を含むテーブルから選択するビューです。 FK列には、ステータス名を含むClaimStatus
テーブルへのインデックスがあります。
COUNT
を使用してもSUM
を使用しても、なぜそんなに大きな違いがあるのですか?
実行計画:
合計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(...)
バージョンは12回インデックスをシークする必要があります(一意のステータスタイプの総数)。
明らかに、インデックスを3回検索する方が、12回検索するよりも高速になります。
最初のプランには238MBのメモリ許可が必要ですが、2番目のプランには650MBのメモリ許可が必要です。それはmayより大きなメモリ許可をすぐに満たすことができず、クエリが非常に遅くなるということです。
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
述語をプランのインデックスシーク部分に押し込むのを本質的に防ぎます。