sql - t-sql GROUP BY 和 COUNT,然后包含 COUNT 中的 MAX

标签 sql sql-server-2008 tsql

假设您有一个包含数十万行的“汽车”表, 你想做一个 GROUP BY:

SELECT   CarID
         , CarName
         , COUNT(*) AS Total
FROM     dbo.tbl_Cars
GROUP BY CarID
         , CarName

分组后的结果类似于:

CarID       CarName    Total
1872        Olds       202,121   
547841      BMW        175,298
9877        Ford        10,241

一切都很好。 但我的问题是,获得这些信息的最佳方式是什么? Total 和 MAX Total 放到一张表中,在性能和 干净的编码,所以你会得到如下结果:

CarID       CarName    Total      Max Total
1872        Olds       202,121    202,121
547841      BMW        175,298    202,121
9877        Ford        10,241    202,121 

一种方法是将 GROUP 结果放入临时表中, 然后将临时表中的 MAX 获取到局部变量中。 但我想知道做到这一点的最佳方法是什么。

<小时/>

更新

公用表表达式似乎写起来最优雅, 但与@EBarr 类似,我有限的测试表明性能明显较慢。 所以我不会选择 CTE。

正如 @EBarr 为 COMPUTE 选项提供的链接所示,该功能 已被弃用,这似乎也不是最好的路线。

局部变量的 MAX 值的选项和使用 临时表可能是我走的路线,因为我不是 意识到它的性能问题。

关于我的用例的更多细节:它可能最终会成为 系列其他问题。但足以说明我正在加载 将大量数据子集放入临时表中(因此 tbl_Cars 的子集是 进入#tbl_Cars,甚至#tbl_Cars可能会被进一步过滤 并对其执行聚合),因为我必须执行多个过滤 以及在单个存储过程中对其进行聚合查询 返回多个结果集。

<小时/>

更新2

@EBarr 对窗口函数的使用很好而且简短。注意 self : 如果使用 RIGHT JOIN 到外部引用表,则 COUNT() 函数应从 tbl_Cars 中选择一列,而不是 '*'

SELECT       M.MachineID
             , M.MachineType
             , COUNT(C.CarID) AS Total
             , MAX(COUNT(C.CarID)) OVER() as MaxTotal
FROM         dbo.tbl_Cars C
RIGHT JOIN   dbo.tbl_Machines M
      ON     C.CarID = M.CarID
GROUP BY     M.MachineID
             , M.MachineType

就速度而言,似乎还不错,但到什么程度你必须 担心阅读量?

最佳答案

从机制上讲,有几种方法可以做到这一点。您可以使用临时表/表变量。另一种方法是使用嵌套查询和/或 CTE,如 @Aaron_Bertrand 所示。第三种方法是使用窗口函数,例如...

SELECT    CarName,
          COUNT(*) as theCount,
          MAX(Count(*)) OVER(PARTITION BY 'foo') as MaxPerGroup
FROM      dbo.tbl_Cars
GROUP BY CarName

不受欢迎(阅读废弃的)第四种方法是使用 COMPUTE 关键字......

SELECT   CarID, CarName, Count(*)
FROM     dbo.tbl_Cars
GROUP BY CarID, CarName 
COMPUTE MAX(Count(*))   

COMPUTE关键字生成的总计显示为结果集末尾的附加摘要列 ( see this )。在上面的查询中,您实际上会看到两个记录集。

最快

现在,下一个问题是什么是“最好/最快/最简单”。我立即想到一个indexed view 。正如@Aaron 温柔地提醒我的那样,索引 View 有各种各样的限制。然而,上述策略允许您在 SELECT...FROM..GROUP BY 上创建索引 View 。然后从索引 View 中选择应用 WINDOWED FUNCTION 子句。

但是,如果不了解更多有关您的设计的信息,任何人都很难告诉您什么是最好的。您将从索引 View 中获得快速查询。不过,这种性能是有代价的。价格是维护费用。如果基础表是大量插入/更新/删除操作的目标,则索引 View 的维护将降低其他区域的性能。

如果您分享更多有关您的用例和数据访问模式的信息,人们将能够分享更多见解。

<小时/>

微观性能测试

因此,我生成了一个小数据脚本,并查看了 CTE 性能与窗口函数的 SQL 分析器编号。这是一个微观测试,因此请在实际负载下尝试系统中的一些实数。

数据生成:

Create table Cars ( CarID int identity (1,1) primary key, 
                    CarName varchar(20), 
                    value int)
GO
insert into Cars (CarName, value)
values  ('Buick', 100),
        ('Ford', 10),
        ('Buick', 300),     
        ('Buick', 100),
        ('Pontiac', 300),       
        ('Bmw', 100),
        ('Mecedes', 300),       
        ('Chevy', 300),     
        ('Buick', 100),
        ('Ford', 200);
GO 1000

此脚本生成 10,000 行。然后,我多次运行以下四个查询中的每一个:

--just group by
select  CarName,COUNT(*) countThis
FROM    Cars
GROUP BY CarName        

--group by with compute (BAD BAD DEVELOPER!)
select  CarName,COUNT(*) countThis
FROM    Cars
GROUP BY CarName        
COMPUTE  MAX(Count(*));

-- windowed aggregates...
SELECT  CarName,
        COUNT(*) as theCount,
        MAX(Count(*)) OVER(PARTITION BY 'foo') as MaxInAnyGroup
FROM Cars
GROUP BY CarName        

--CTE version
;WITH x AS (
  SELECT   CarName,
           COUNT(*) AS Total
  FROM     Cars
  GROUP BY CarName
)
SELECT x.CarName, x.Total, x2.[Max Total]
FROM x CROSS JOIN (
  SELECT [Max Total] = MAX(Total) FROM x
) AS x2;

运行上述查询后,我在上面的“just group by”查询上创建了一个索引 View 。然后我在执行 MAX(Count(*)) OVER(PARTITION BY 'foo' 的索引 View 上运行查询。 .

平均结果

Query                      CPU       Reads     Duration   
--------------------------------------------------------
Group By                   15        31        7 ms  
Group & Compute            15        31        7 ms
Windowed Functions         14        56        8 ms 
Common Table Exp.          16        62       15 ms
Windowed on Indexed View    0        24        0 ms

显然,这是一个微基准,只有轻微的指导意义,因此请充分考虑它的值(value)。

关于sql - t-sql GROUP BY 和 COUNT,然后包含 COUNT 中的 MAX,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9217486/

相关文章:

java - 执行update返回1但不更新

mysql - 使用 pivot 命令时出现动态 SQL 错误

entity-framework - Entity Framework 生成低效的 SQL

sql - 找出这两个涉及日期时间的查询之间的区别

sql - Postgresql 应用程序插入和触发器性能

sql - 从 Getdate() 获取时间

sql - 在 MS-SQL 中将行数据分组并求和到列中?

.net - 销售点应用架构建议

sql - 无法解决临时表和 sys.objects 之间的排序规则冲突