如何编写一个查询,在多组行中聚合具有最新日期的单行?

时间:2013-04-03 07:25:41

标签: mysql sql

我有一个MySQL表,每个人都有很多行,我想写一个聚合具有特殊约束的行的查询。 (每人一个)

例如,假设该表包含以下数据。

name   date                    reason
---------------------------------------
John   2013-04-01 14:00:00     Vacation
John   2013-03-31 18:00:00     Sick
Ted    2012-05-06 20:00:00     Sick
Ted    2012-02-20 01:00:00     Vacation
John   2011-12-21 00:00:00     Sick
Bob    2011-04-02 20:00:00     Sick

我想看看'reason'栏的分布情况。如果我只是写下面的查询

select reason, count(*) as count from table group by reason

然后我将能够看到整个表格的原因。

reason       count
------------------
Sick         4
Vacation     2

但是,我只对每个人的单一理由感兴趣。应该计算的原因应该是来自该人员记录的最新日期。例如,John的最新理由是Vacation,而Ted的最新理由是Sick。鲍勃的最新理由(也是唯一的原因)是Sick

该查询的预期结果应如下所示。 (计数总和将为3,因为只有3人)

reason      count
-----------------
Sick        2
Vacation    1

当我想查看原因的分布(计数)时,是否可以编写一个查询,以便计算单个最新原因?

以下是关于该表的一些事实。

  • 该表有数千万行
  • 大多数时候,每个人都有一个理由。
  • 有些人有多种原因,但99.99%的人有少于5个理由。
  • 有数百万个不同的名字,大约有30种不同的原因。
  • 该表根据日期范围进行分区。

5 个答案:

答案 0 :(得分:1)

SELECT T.REASON, COUNT(*) 
FROM
(
 SELECT PERSON, MAX(DATE) AS MAX_DATE
 FROM TABLE-NAME
 GROUP BY PERSON
) A, TABLE-NAME T
WHERE T.PERSON = A.PERSON AND T.DATE = A.MAX_DATE
GROUP BY T.REASON

答案 1 :(得分:0)

试试这个

select reason, count(*) from 
(select reason from table where date in 
    (select max(date) from table group by name)) t 
group by reason

答案 2 :(得分:0)

您正在寻找的解决方案似乎可以通过此查询解决:

select 
    reason, 
    count(*) 
from (select * from tablename group by name) abc 
group by 
    reason

它非常快速而简单。您可以查看SQL Fiddle

答案 3 :(得分:0)

在MySQL中,由于您无法访问partitionning query in SQL ServerOracle等工具,因此执行此类查询效率不高。
您仍然可以通过执行子查询来模拟它,并根据您需要的条件检索行,这里是最大日期:

SELECT t.reason, COUNT(1) 
FROM
(
     SELECT name, MAX(adate) AS maxDate
     FROM @aTable
     GROUP BY name
) maxDateRows
    INNER JOIN @aTable t ON maxDateRows.name = t.name
                        AND maxDateRows.maxDate = t.adate
GROUP BY t.reason

You can see a sample here.
在你的样品上测试这个查询,但我担心它会慢下来。

为了您的信息,您可以在SQL Server中以更优雅,更快的方式执行相同的操作:

SELECT reason, COUNT(1)
FROM
(
     SELECT name
          , reason
          , RANK() OVER(PARTITION BY name ORDER BY adate DESC) as Rank
     FROM @aTable
     ) AS rankTable
WHERE Rank = 1
GROUP BY reason

The sample is here

如果您真的遇到MySql,并且第一个查询太慢,那么您可以解决问题。

创建表格的第一个查询:

CREATE TABLE maxDateRows AS
SELECT name, MAX(adate) AS maxDate
FROM @aTable
GROUP BY name

然后在name和maxDate上创建索引 最后,得到结果:

SELECT t.reason, COUNT(1) 
FROM maxDateRows m
    INNER JOIN @aTable t ON m.name = t.name
                        AND m.maxDate = t.adate
GROUP BY t.reason

答案 4 :(得分:0)

如果此答案与现有答案重复,请道歉。也许我患有某种形式的失语症,但我看不到它......

SELECT x.reason
     , COUNT(*) 
  FROM absentism x 
  JOIN 
     ( SELECT name,MAX(date) max_date FROM absentism GROUP BY name) y
    ON y.name = x.name 
   AND y.max_date = x.date 
 GROUP 
    BY reason;