CASE ile birden fazla COUNT birden fazla neden daha hızlı?


14

Aşağıdaki iki yaklaşımdan hangisinin daha hızlı olduğunu bilmek istedim:

1) Üç 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) SUMile FROM-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;

Aradaki farkın çok büyük olması beni şaşırttı. Üç alt sorgulu ilk sorgu hemen sonucu döndürür, ikinci SUMyaklaşım ise 18 saniyeye ihtiyaç duyar.

Claims~ 18 milyon satır içeren bir tablodan seçilen bir görünümdür. FK-Sütununda ClaimStatus, durum adını içeren tabloya ilişkin bir dizin vardır .

Neden ben kullanmak ister böyle büyük bir fark yaratır mı COUNTyoksa SUM?

Yürütme-planları:

Toplam 12 durum vardır. Bu üç durum tüm satırların% 7'sine aittir.


Bu gerçek görünüm, alakalı olup olmadığından emin değilim:

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 

Her iki bağlantı COUNTda planın sürümünü gösteriyor gibi görünüyor . SUMDoğru planı işaret etmek için beğeniyi sürüme düzenleyebilir misiniz ?
Geoff Patterson

Bu üç statii olan satırların diğer statii olan sıralara oranı nedir?
Max Vernon

1
@ MaxVernon: evet, elbette, çok fazla sıfır gördüm, haklısın. Yorumlarımı sileyim. Evet, başka bir durumda 16,7 milyon satır var. Çoğu Authorized.
Tim Schmelter

2
İkinci planın tüm tabloyu 12 kez taramak zorunda kaldığını tahmin ediyorum (şov budur). Bu muhtemelen yüklemleri taramayı zorlayamamaktan geliyor. Eklersen gibi performans nedir WHERE c.Status = 'Approved' or c.Status = 'Valid' or c.status = 'Reject'için SUMvaryant.
Max Vernon

@ MaxVernon: Toplamda on iki durum vardır. Bu benim için gerçekten bir sorun değil, ama optimize edicinin bunu başaramadığı çok şaşırdım. Gerçekten yürütme planı analiz becerilerim üzerinde çalışmalıyım. Bir cevap verin. Varsayımınız nedir, SQL Server neden sadece üç durumu tarayamıyor?
Tim Schmelter

Yanıtlar:


19

COUNT(*)Oysa versiyonu kez seçilerek her durum için basitçe durum sütununda sahip endeksi içine aramaya yapabiliyor SUM(...)versiyon ihtiyaçları indeksi oniki defa (benzersiz durum türleri toplam sayısı) aramaya.

Açıkça bir endeksi üç kez aramak, 12 kez aramaktan daha hızlı olacaktır.

İlk plan 238MB'lık bir bellek desteği gerektirirken ikinci plan 650MB'lık bir bellek desteği gerektirir. Bu olabilir büyük bellek verme derhal doldurulması çok daha yavaş olduğunu sorguyu yapma olamayacağı olun.

İkinci sorguyu şu şekilde değiştirin:

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';

Bu, sorgu iyileştiricinin dizinin% 75'ini ortadan kaldırmasına olanak tanır ve hem daha düşük bir bellek desteği, hem de daha düşük G / Ç gereksinimleri ve daha hızlı sonuç süresi ile sonuçlanmalıdır.

SUM(CASE WHEN ...)Yapı, esas olarak itme sorgu iyileştiricisi önler Statusplanının kısmını talep dizin içine yüklemler'ıN aşağı.


Güzel bellek ile yakalamak. 32GB'ımın şu anda kullanımda olduğunu fark ettim (sadece 300 MB boş alan). Düzenle Ancak, bazı bellek serbest bıraktım. Sonuç aynı
Tim Schmelter

Seçeneğe bakmak isteyebilirsiniz max server memory- sisteminiz için doğru değere yapılandırılmalıdır. Bunun nasıl yapılacağıyla ilgili ayrıntılar için bu soruya ve cevaplarına bakmak isteyebilirsiniz .
Max Vernon

1
Ne yazık ki bu sunucu sadece veritabanı için değil, SSAS küpü ve bazı araçlar için de (intranet web uygulaması dahil) kullanılmaktadır. Ama zaten maksimum olarak 12GB atadım.
Tim Schmelter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.