关于mysql:如何在SQL中用另一列选择MAX(列值),DISTINCT的行?

How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?

我的表是:

1
2
3
4
5
6
7
8
9
10
id  home  datetime     player   resource
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
3  | 10  | 03/03/2009 | john   | 300
4  | 11  | 03/03/2009 | juliet | 200
6  | 12  | 03/03/2009 | borat  | 500
7  | 13  | 24/12/2008 | borat  | 600
8  | 13  | 01/01/2009 | borat  | 700

我需要选择每个不同的home,保持datetime的最大值。

结果是:

1
2
3
4
5
6
id  home  datetime     player   resource
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
8  | 13  | 01/01/2009 | borat  | 700

我已经尝试过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 1 ..by the MySQL manual:

SELECT DISTINCT
  home,
  id,
  datetime AS dt,
  player,
  resource
FROM topten t1
WHERE datetime = (SELECT
  MAX(t2.datetime)
FROM topten t2
GROUP BY home)
GROUP BY datetime
ORDER BY datetime DESC

不起作用。结果集有130行,但数据库有187行。结果包括home的一些副本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 2 ..join

SELECT
  s1.id,
  s1.home,
  s1.datetime,
  s1.player,
  s1.resource
FROM topten s1
JOIN (SELECT
  id,
  MAX(datetime) AS dt
FROM topten
GROUP BY id) AS s2
  ON s1.id = s2.id
ORDER BY datetime

不。提供所有记录。

1
-- 3 ..something exotic:

有各种各样的结果。


你太亲密了!您所需要做的只是同时选择主页及其最长日期时间,然后在这两个字段上重新连接到topten表:

1
2
3
4
5
6
7
8
SELECT tt.*
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt
ON tt.home = groupedtt.home
AND tt.datetime = groupedtt.MaxDateTime


下面是T-SQL版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- Test data
DECLARE @TestTable TABLE (id INT, home INT, date DATETIME,
  player VARCHAR(20), resource INT)
INSERT INTO @TestTable
SELECT 1, 10, '2009-03-04', 'john', 399 UNION
SELECT 2, 11, '2009-03-04', 'juliet', 244 UNION
SELECT 5, 12, '2009-03-04', 'borat', 555 UNION
SELECT 3, 10, '2009-03-03', 'john', 300 UNION
SELECT 4, 11, '2009-03-03', 'juliet', 200 UNION
SELECT 6, 12, '2009-03-03', 'borat', 500 UNION
SELECT 7, 13, '2008-12-24', 'borat', 600 UNION
SELECT 8, 13, '2009-01-01', 'borat', 700

-- Answer
SELECT id, home, date, player, resource
FROM (SELECT id, home, date, player, resource,
    RANK() OVER (PARTITION BY home ORDER BY date DESC) N
    FROM @TestTable
)M WHERE N = 1

-- and if you really want only home with max date
SELECT T.id, T.home, T.date, T.player, T.resource
    FROM @TestTable T
INNER JOIN
(   SELECT TI.id, TI.home, TI.date,
        RANK() OVER (PARTITION BY TI.home ORDER BY TI.date) N
    FROM @TestTable TI
    WHERE TI.date IN (SELECT MAX(TM.date) FROM @TestTable TM)
)TJ ON TJ.N = 1 AND T.id = TJ.id

编辑不幸的是,MySQL中没有rank()over函数。但是它可以被仿真,参见使用MySQL仿真分析(aka ranking)函数。所以这是MySQL版本:

1
2
3
4
5
6
7
SELECT id, home, date, player, resource
FROM TestTable AS t1
WHERE
    (SELECT COUNT(*)
            FROM TestTable AS t2
            WHERE t2.home = t1.home AND t2.date > t1.date
    ) = 0


最快的MySQL解决方案,无内部查询,无GROUP BY

1
2
3
4
5
6
SELECT m.*                    -- get the row that contains the max value
FROM topten m                 --"m" from"max"
    LEFT JOIN topten b        --"b" from"bigger"
        ON m.home = b.home    -- match"max" row with"bigger" row by `home`
        AND m.datetime < b.datetime           -- want"bigger" than"max"
WHERE b.datetime IS NULL      -- keep only if there is no bigger than max

说明:

使用home列将表与其自身联接。使用LEFT JOIN确保表m中的所有行都出现在结果集中。如果表b中没有匹配项,则在b的列中将有NULL

JOIN上的另一个条件要求只匹配b列中在DATETIME列上的值大于m列中的值的行。

使用问题中发布的数据,LEFT JOIN将生成这对:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+------------------------------------------+--------------------------------+
|              the row from `m`            |    the matching row from `b`   |
|------------------------------------------|--------------------------------|
| id  home  datetime     player   resource | id    home   datetime      ... |
|----|-----|------------|--------|---------|------|------|------------|-----|
| 1  | 10  | 04/03/2009 | john   | 399     | NULL | NULL | NULL       | ... | *
| 2  | 11  | 04/03/2009 | juliet | 244     | NULL | NULL | NULL       | ... | *
| 5  | 12  | 04/03/2009 | borat  | 555     | NULL | NULL | NULL       | ... | *
| 3  | 10  | 03/03/2009 | john   | 300     | 1    | 10   | 04/03/2009 | ... |
| 4  | 11  | 03/03/2009 | juliet | 200     | 2    | 11   | 04/03/2009 | ... |
| 6  | 12  | 03/03/2009 | borat  | 500     | 5    | 12   | 04/03/2009 | ... |
| 7  | 13  | 24/12/2008 | borat  | 600     | 8    | 13   | 01/01/2009 | ... |
| 8  | 13  | 01/01/2009 | borat  | 700     | NULL | NULL | NULL       | ... | *
+------------------------------------------+--------------------------------+

最后,WHERE子句只保留b列中有NULLs的对(上表中用*标记);这意味着,由于JOIN子句的第二个条件,从m中选择的行在DATETIME列中具有最大值。

阅读SQL反模式:避免数据库编程书中的陷阱以获取其他SQL提示。


即使每个home有两行或多行,且DATETIME相等,也可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT id, home, datetime, player, resource
FROM   (
       SELECT (
              SELECT  id
              FROM    topten ti
              WHERE   ti.home = t1.home
              ORDER BY
                      ti.datetime DESC
              LIMIT 1
              ) lid
       FROM   (
              SELECT  DISTINCT home
              FROM    topten
              ) t1
       ) ro, topten t2
WHERE  t2.id = ro.lid


我认为这会给你期望的结果:

1
2
3
SELECT   home, MAX(datetime)
FROM     my_table
GROUP BY home

但如果您还需要其他列,只需与原始表进行联接(检查Michael La Voie答案)

最好的问候。


因为人们似乎一直在关注这个话题(评论日期从1.5年开始),这就不简单了:

SELECT * FROM (SELECT * FROM topten ORDER BY datetime DESC) tmp GROUP BY home

不需要聚合函数…

干杯。


您也可以尝试这个方法,对于大表,查询性能会更好。当每个家庭的记录不超过两个,并且日期不同时,它就会工作。更好的一般mysql查询是来自上面的michael la voie。

1
2
3
4
5
SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
FROM   t_scores_1 t1
INNER JOIN t_scores_1 t2
   ON t1.home = t2.home
WHERE t1.date > t2.date

如果是Postgres或提供分析功能的DBS,请尝试

1
2
3
4
5
6
7
8
9
SELECT t.* FROM
(SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
  , row_number() over (partition by t1.home order by t1.date desc) rw
 FROM   topten t1
 INNER JOIN topten t2
   ON t1.home = t2.home
 WHERE t1.date > t2.date
) t
WHERE t.rw = 1


这适用于Oracle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
with table_max as(
  select id
       , home
       , datetime
       , player
       , resource
       , max(home) over (partition by home) maxhome
    from table  
)
select id
     , home
     , datetime
     , player
     , resource
  from table_max
 where home = maxhome


请为SQL Server尝试此操作:

1
2
3
4
WITH cte AS (
   SELECT home, MAX(year) AS year FROM Table1 GROUP BY home
)
SELECT * FROM Table1 a INNER JOIN cte ON a.home = cte.home AND a.year = cte.year

1
2
3
4
5
6
7
8
9
10
11
SELECT  tt.*
FROM    TestTable tt
INNER JOIN
        (
        SELECT  coord, MAX(datetime) AS MaxDateTime
        FROM    rapsa
        GROUP BY
                krd
        ) groupedtt
ON      tt.coord = groupedtt.coord
        AND tt.datetime = groupedtt.MaxDateTime

另一种方式到现在每行集团GT的最近使用的子query,basically calculates的秩为每行每集团,然后过滤出你现在最近rows AS与秩= 1 P / < >

1
2
3
4
5
6
7
8
select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and a.`datetime` < b.`datetime`
) +1 = 1

演示 P / < >

这里的视觉演示为排名没有为每行为更好的理解 P / < >

By reading some comments what about if there are two rows which have same 'home' and 'datetime' field values?

在query将失败和回报将超过1 rows为上面的情况。到盖起来,这样的情况将有需要的另一个parameter /标准/柱过程中的决定,行应该采取的瀑布,在上面的情况。通过viewing的样本数据集)为此承担有主要的关键id柱过程中,应该设定到汽车increment。所以,我们可以使用这个柱过程中挑选到现在最近的行由tweaking一样query与帮助CASE像statement of P / < >

1
2
3
4
5
6
7
8
9
10
11
12
select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and  case
       when a.`datetime` = b.`datetime`
       then a.id < b.id
       else a.`datetime` < b.`datetime`
       end
) + 1 = 1

演示 P / < >

在query将选择行与highest id among同样datetime值 P / < >

视觉演示为排名没有为每一行 P / < >


1
2
3
SELECT c1, c2, c3, c4, c5 FROM table1 WHERE c3 = (select max(c3) from table)

SELECT * FROM table1 WHERE c3 = (select max(c3) from table1)


这里是mysql版本,它只打印一个条目,其中一个组中有重复的max(datetime)。

你可以在这里测试http://www.sqlpiddle.com/!2/0A4AE/1

样本数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> SELECT * from topten;
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    3 |   10 | 2009-03-03 00:00:00 | john   |      300 |
|    4 |   11 | 2009-03-03 00:00:00 | juliet |      200 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    6 |   12 | 2009-03-03 00:00:00 | borat  |      500 |
|    7 |   13 | 2008-12-24 00:00:00 | borat  |      600 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+

带用户变量的MySQL版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SELECT *
FROM (
    SELECT ord.*,
        IF (@prev_home = ord.home, 0, 1) AS is_first_appear,
        @prev_home := ord.home
    FROM (
        SELECT t1.id, t1.home, t1.player, t1.resource
        FROM topten t1
        INNER JOIN (
            SELECT home, MAX(datetime) AS mx_dt
            FROM topten
            GROUP BY home
          ) x ON t1.home = x.home AND t1.datetime = x.mx_dt
        ORDER BY home
    ) ord, (SELECT @prev_home := 0, @seq := 0) init
) y
WHERE is_first_appear = 1;
+------+------+--------+----------+-----------------+------------------------+
| id   | home | player | resource | is_first_appear | @prev_home := ord.home |
+------+------+--------+----------+-----------------+------------------------+
|    9 |   10 | borat  |      700 |               1 |                     10 |
|   10 |   11 | borat  |      700 |               1 |                     11 |
|   12 |   12 | borat  |      700 |               1 |                     12 |
|    8 |   13 | borat  |      700 |               1 |                     13 |
+------+------+--------+----------+-----------------+------------------------+
4 rows in set (0.00 sec)

接受的答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT tt.*
FROM topten tt
INNER JOIN
    (
    SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home
) groupedtt ON tt.home = groupedtt.home AND tt.datetime = groupedtt.MaxDateTime
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+
7 rows in set (0.00 sec)


为什么不使用:选择home,max(datetime)作为maxdatetime,player,resource from topten group by home我错过什么了吗?


试试这个

1
2
3
4
5
select * from mytable a join
(select home, max(datetime) datetime
from mytable
group by home) b
 on a.home = b.home and a.datetime = b.datetime

当做K


这是您需要的查询:

1
2
3
4
5
6
7
 SELECT b.id, a.home,b.[datetime],b.player,a.resource FROM
 (SELECT home,MAX(resource) AS resource FROM tbl_1 GROUP BY home) AS a

 LEFT JOIN

 (SELECT id,home,[datetime],player,resource FROM tbl_1) AS b
 ON  a.resource = b.resource WHERE a.home =b.home;


@在大多数情况下,被接受的答案都会很好地解决,但下面的一个答案却失败了。

如果有两行的homeID和datetime相同,那么查询将返回两行,而不是所需的不同homeID,如下所示。

1
2
3
4
5
6
7
8
SELECT DISTINCT tt.home  , tt.MaxDateTime
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt
ON tt.home = groupedtt.home
AND tt.datetime = groupedtt.MaxDateTime