为什么使用CASE,多个COUNT比一个SUM快?


14

我想知道以下两种方法中哪一种速度更快:

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

令我惊讶的是差异如此之大。具有三个子查询的第一个查询立即返回结果,而第二种SUM方法则需要18秒。

Claims是从包含约1800万行的表中选择的视图。该ClaimStatus表的FK列上有一个索引,其中包含状态名称。

无论我使用COUNT还是使用它,为什么会有如此大的不同SUM

执行计划:

共有12个状态。这三个状态占所有行的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该计划的版本。您可以将like修改为SUM版本以指向正确的计划吗?
2015年

具有这三个状态的行与具有其他状态的行的比率是多少?
Max Vernon

1
@MaxVernon:是的,当然,我看过太多零,你是对的。让我删除我的评论。是的,其他状态的行数为1670万。大多数是Authorized
蒂姆·施密特

2
我估计第二个计划将不得不扫描整个表12次(即显示的结果)。这可能是由于无法将谓词下推到扫描中而引起的。如果添加WHERE c.Status = 'Approved' or c.Status = 'Valid' or c.status = 'Reject'SUM变体,性能如何?
Max Vernon

@MaxVernon:共有十二种状态。对我来说,这不是真正的问题,但是我很惊讶优化器无法处理此问题。我应该真正地执行我的执行计划分析技能。做出答案。您的假设是什么,为什么SQL Server无法仅扫描三种状态?
蒂姆·施密特

Answers:


19

COUNT(*)版本可以简单地针对您选择的每个状态在状态列中查找一次索引,而该SUM(...)版本则需要十二次查找索引(唯一状态类型的总数)。

显然,寻找索引的三倍将比寻找索引的12倍快。

第一个计划需要238MB的内存授权,而第二个计划需要650MB的内存授权。这可能是更大的内存授权不能立即填补,使得查询慢得多。

将第二个查询更改为:

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可用空间)。编辑但是,我释放了一些内存。结果是一样的
Tim Schmelter,2015年

您可能需要查看该max server memory选项-应该为您的系统将其配置为正确的值。您可能需要查看问题以及有关如何执行此操作的详细信息的答案。
Max Vernon

1
不幸的是,该服务器不仅用于数据库,而且还用于SSAS多维数据集和某些工具(包括Intranet Web应用程序)。但是我已经分配了最大12GB的内存。
蒂姆·施梅尔特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.