Tại sao nhiều COUNT nhanh hơn một SUM với CASE?


14

Tôi muốn biết cách tiếp cận nào trong hai cách sau nhanh hơn:

1) Ba 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) SUMvới FROM-cách:

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;

Tôi đã ngạc nhiên rằng sự khác biệt là rất lớn. Truy vấn đầu tiên với ba truy vấn con trả về kết quả ngay lập tức trong khi SUMcách tiếp cận thứ hai cần 18 giây.

Claimslà một khung nhìn chọn từ một bảng chứa ~ 18 triệu hàng. Có một chỉ mục trên Cột FK cho ClaimStatusbảng chứa tên trạng thái.

Tại sao nó làm cho một sự khác biệt lớn như vậy cho dù tôi sử dụng COUNThay SUM?

Kế hoạch thực hiện:

Có tổng cộng 12 trạng thái. Ba trạng thái này thuộc về 7% của tất cả các hàng.


Đây là chế độ xem thực tế, tôi không chắc nó có liên quan hay không:

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 

Có vẻ như cả hai liên kết đều chỉ đến COUNTphiên bản của kế hoạch. Bạn có thể chỉnh sửa like cho SUMphiên bản để chỉ ra kế hoạch chính xác không?
Geoff Patterson

Tỷ lệ của các hàng với ba statii đó là bao nhiêu so với các hàng với statii khác?
Max Vernon

1
@MaxVernon: vâng, tất nhiên, tôi đã thấy quá nhiều số không, bạn nói đúng. Hãy để tôi xóa ý kiến ​​của tôi. Có, có 16,7 triệu hàng trong trạng thái khác. Hầu hết là Authorized.
Tim Schmelter

2
Tôi sẽ ước tính kế hoạch thứ hai đang phải chịu đựng khi quét toàn bộ bảng 12 lần (đó là những gì được hiển thị). Điều này có khả năng đến từ việc không thể đẩy các vị từ xuống quét. Hiệu suất như thế nào nếu bạn thêm WHERE c.Status = 'Approved' or c.Status = 'Valid' or c.status = 'Reject'vào SUMbiến thể.
Max Vernon

@MaxVernon: có tổng cộng mười hai trạng thái. Nó không thực sự là một vấn đề đối với tôi, nhưng tôi đã rất ngạc nhiên khi trình tối ưu hóa không thể xử lý việc này. Tôi thực sự nên làm việc với các kỹ năng phân tích kế hoạch thực hiện của mình. Làm cho nó một câu trả lời. Giả định của bạn là gì, tại sao SQL-Server không thể quét chỉ ba trạng thái?
Tim Schmelter

Câu trả lời:


19

Các COUNT(*)phiên bản có thể chỉ đơn giản là tìm kiếm vào chỉ số mà bạn có trong cột trạng thái một lần cho mỗi trạng thái mà bạn đang chọn, trong khi SUM(...)nhu cầu phiên bản để tìm kiếm các chỉ số mười hai lần (tổng số loại trạng thái duy nhất).

Rõ ràng tìm kiếm một chỉ số ba lần sẽ nhanh hơn tìm kiếm 12 lần.

Gói đầu tiên yêu cầu cấp bộ nhớ 238MB, trong khi gói thứ hai yêu cầu cấp bộ nhớ là 650 MB. Có thể là cấp bộ nhớ lớn hơn không thể được điền ngay lập tức, làm cho truy vấn chậm hơn nhiều.

Thay đổi truy vấn thứ hai thành:

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

Điều này sẽ cho phép trình tối ưu hóa truy vấn loại bỏ 75% tìm kiếm chỉ mục và sẽ dẫn đến cả việc cấp bộ nhớ được yêu cầu thấp hơn, yêu cầu I / O thấp hơn và thời gian kết quả nhanh hơn.

Cấu SUM(CASE WHEN ...)trúc về cơ bản ngăn chặn trình tối ưu hóa truy vấn đẩy các Statusvị từ xuống phần tìm kiếm chỉ mục của kế hoạch.


Bắt đẹp với ký ức. Tôi đã nhận thấy rằng tất cả 32 GB của tôi hiện đang được sử dụng (chỉ có 300 MB miễn phí). Chỉnh sửa Tuy nhiên, tôi đã giải phóng một số bộ nhớ. Kết quả là như nhau
Tim Schmelter

Bạn có thể muốn xem xét max server memorytùy chọn - nó nên được cấu hình theo giá trị chính xác cho hệ thống của bạn. Bạn có thể muốn xem xét câu hỏi này và câu trả lời để biết chi tiết về cách làm điều đó.
Max Vernon

1
Thật không may, máy chủ này không chỉ được sử dụng cho cơ sở dữ liệu mà còn cho khối SSAS và một số công cụ (bao gồm cả ứng dụng web mạng nội bộ). Nhưng tôi đã chỉ định tối đa 12GB.
Tim Schmelter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.