Pregunta ¿Cómo puedo SELECCIONAR filas con MAX (valor de columna), DISTINCT por otra columna en SQL?


Mi mesa es: 

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

Necesito seleccionar cada uno home sosteniendo el valor máximo de datetime.

El resultado sería: 

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

Yo he tratado:

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

No funciona Result-set tiene 130 filas aunque la base de datos contiene 187. El resultado incluye algunos duplicados de home.

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

Nop. Da todos los registros.

-- 3 ..something exotic: 

Con varios resultados.


643
2018-03-04 20:14


origen


Respuestas:


¡Estás muy cerca! Todo lo que necesita hacer es seleccionar AMBAS la casa y su fecha máxima, luego unirse de nuevo a la topten tabla en AMBOS campos:

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

789
2018-03-04 20:22



Aquí va T-SQL versión:

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

EDITAR
Desafortunadamente, no hay función RANK () OVER en MySQL.
Pero puede ser emulado, ver Emulación de funciones analíticas (Clasificación AKA) con MySQL.
Entonces esto es MySQL versión:

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

67
2018-03-04 20:59



El más rápido MySQL solución, sin consultas internas y sin GROUP BY:

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

Explicación:

Únete a la mesa consigo mismo usando home columna. El uso de LEFT JOIN asegura todas las filas de la mesa m aparecer en el conjunto de resultados. Aquellos que no tienen un partido en la mesa b tendrá NULLs para las columnas de b.

La otra condición en el JOIN pide que coincida solo con las filas de b que tienen un mayor valor en el datetime columna que la fila de m.

Usando los datos publicados en la pregunta, el LEFT JOIN producirá estos pares:

+------------------------------------------+--------------------------------+
|              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       | ... | *
+------------------------------------------+--------------------------------+

Finalmente, el WHERE cláusula mantiene solo los pares que tienen NULLs en las columnas de b (están marcados con * en la tabla de arriba); esto significa, debido a la segunda condición de la JOIN cláusula, la fila seleccionada de m tiene el mayor valor en la columna datetime.

Leer el Antipatterns de SQL: evitar las trampas de la programación de bases de datos reservar para otros consejos de SQL.


50
2018-01-06 16:23



Esto funcionará incluso si tiene dos o más filas para cada home con igual DATETIMEde:

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

24
2018-03-04 20:24



Creo que esto te dará el resultado deseado:

SELECT   home, MAX(datetime)
FROM     my_table
GROUP BY home

PERO si necesita otras columnas también, solo haga una combinación con la tabla original (verifique Michael La Voie responder)

Atentamente.


20
2018-03-04 20:30



Dado que las personas parecen seguir corriendo en este hilo (la fecha del comentario oscila entre 1,5 años) no es mucho más simple:

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

No se necesitan funciones de agregación ...

Aclamaciones.


12
2017-12-05 17:04



También puede probar este y para tablas grandes el rendimiento de las consultas será mejor. Funciona cuando no hay más de dos registros para cada hogar y sus fechas son diferentes. Mejor consulta general de MySQL es una de Michael La Voie anterior.

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

O en el caso de Postgres o aquellos dbs que proporcionan funciones analíticas, intente

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

8
2018-02-16 20:51



Esto funciona en Oracle:

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

6
2018-03-05 23:19



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

5
2018-03-04 20:58