it-swarm.com.de

Wie kann ich Zeilen mit MAX (Column value), DISTINCT durch eine andere Spalte in SQL AUSWÄHLEN?

Mein Tisch ist:  

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

Ich muss jeden einzelnen home auswählen, der den Maximalwert von datetime enthält. 

Ergebnis wäre:  

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

Ich habe versucht:

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

Funktioniert nicht Ergebnismenge hat 130 Zeilen, obwohl die Datenbank 187. .__ enthält. Ergebnis enthält einige Duplikate von 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 

Nee. Gibt alle Datensätze aus.

-- 3 ..something exotic: 

Mit verschiedenen Ergebnissen.

678
Kaptah

Du bist so nah! Alles, was Sie tun müssen, ist, BEIDE die Heimat und die maximale Datumszeit auszuwählen, und dann in die Tabelle topten in den BEIDEN Feldern zurückzukehren:

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
835
Michael La Voie

Hier geht die T-SQL Version:

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

EDIT
Leider gibt es in MySQL keine RANK () OVER-Funktion.
Aber es kann emuliert werden, siehe Emulieren von Analysefunktionen (AKA-Ranking) mit MySQL .
Das ist also MySQL version:

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
69
Maksym Gontar

Die schnellste MySQL-Lösung ohne innere Abfragen und ohne 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

Erklärung:

Verknüpfen Sie die Tabelle mit der Spalte home. Die Verwendung von LEFT JOIN stellt sicher, dass alle Zeilen der Tabelle m in der Ergebnismenge angezeigt werden. Diejenigen, die keine Übereinstimmung in der Tabelle b haben, haben NULLs für die Spalten von b.

Die andere Bedingung in der Variablen JOIN fordert dazu auf, nur die Zeilen von b abzugleichen, die einen größeren Wert in der Spalte datetime haben als die Zeile von m.

Unter Verwendung der in der Frage geposteten Daten erzeugt der LEFT JOIN diese Paare:

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

Schließlich behält die WHERE-Klausel nur die Paare bei, die NULLs in den Spalten von b haben (sie sind in der obigen Tabelle mit * markiert); Dies bedeutet, dass aufgrund der zweiten Bedingung aus der JOIN-Klausel die aus m ausgewählte Zeile den größten Wert in der Spalte datetime hat.

Lesen Sie das Buch SQL Antipatterns: Vermeidung der Fallstricke der Datenbankprogrammierung für weitere SQL-Tipps.

60
axiac

Dies funktioniert auch, wenn Sie zwei oder mehr Zeilen für jede home mit gleich DATETIME haben:

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

Ich denke, das wird Ihnen das gewünschte Ergebnis bringen:

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

ABERWenn Sie auch andere Spalten benötigen, machen Sie einfach einen Join mit der Originaltabelle (überprüfen Sie Michael La Voie answer).

Freundliche Grüße.

21

Da die Leute immer wieder auf diesen Thread zu stoßen scheinen (das Kommentardatum reicht von 1,5 Jahren), ist das nicht viel einfacher:

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

Keine Aggregationsfunktionen erforderlich ...

Prost.

16
MJB

Sie können dies auch versuchen und für große Tabellen wird die Abfrageleistung besser. Es funktioniert, wenn für jedes Heim nicht mehr als zwei Datensätze vorhanden sind und die Daten unterschiedlich sind. Eine bessere allgemeine MySQL-Abfrage ist eine von Michael La Voie oben. 

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

Oder im Fall von Postgres oder den dbs, die analytische Funktionen bereitstellen, versuchen Sie es

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

Dies funktioniert auf 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
8
FerranB
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
7
Kaptah

Versuchen Sie dies für SQL Server:

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

Eine andere Möglichkeit, die aktuellste Zeile pro Gruppe zu ermitteln, verwendet eine Unterabfrage, die im Wesentlichen einen Rang für jede Zeile pro Gruppe berechnet und dann die letzten Zeilen mit Rang = 1 herausfiltern

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and a.`datetime` < b.`datetime`
) +1 = 1

DEMO

Hier ist das visuelle Demo für Rang Nr für jede Zeile zum besseren Verständnis 

Durch Lesen einiger Kommentare was ist, wenn es zwei Zeilen gibt, die die gleichen Feldwerte für 'home' und 'datetime' haben?

Die obige Abfrage schlägt fehl und gibt mehr als 1 Zeilen für die obige Situation zurück. Um diese Situation zu vertuschen, muss ein weiteres Kriterium/Parameter/eine Spalte festgelegt werden, um zu entscheiden, welche Zeile in die obige Situation aufgenommen werden sollte. Beim Betrachten des Beispieldatensatzes gehe ich davon aus, dass es eine Primärschlüsselspalte id gibt, die auf auto inkrement gesetzt werden sollte. Daher können wir diese Spalte verwenden, um die letzte Zeile auszuwählen, indem dieselbe Abfrage mit der CASE-Anweisung wie folgt geändert wird

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

DEMO

Bei der obigen Abfrage wird die Zeile mit der höchsten ID unter denselben datetime-Werten ausgewählt 

visuelle Demo für Rang-Nr. für jede Zeile

4
M Khalid Junaid
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)
4
Jr.

Hier ist die MySQL-Version, die nur einen Eintrag druckt, in dem MAX (datetime) in einer Gruppe doppelt vorhanden ist.

Sie könnten hier testen http://www.sqlfiddle.com/#!2/0a4ae/1

Beispieldaten

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-Version mit Benutzervariable

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)

Akzeptierte Antworten 'outout

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)
3
Jason Heo

Warum nicht verwenden: SELECT home, MAX (datetime) AS MaxDateTime, Player, Ressource FROM topten GROUP BY home Habe ich etwas verpasst?

1
Roland

Versuche dies

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

Grüße K

1
Khb

dies ist die Abfrage, die Sie benötigen:

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

@Michae Die akzeptierte Antwort funktioniert in den meisten Fällen einwandfrei, schlägt jedoch fehl, wie unten beschrieben.

Wenn zwei Zeilen mit HomeID und Datetime identisch waren, gibt die Abfrage beide Zeilen zurück, nicht aber HomeID, wie erforderlich, und fügen Sie in der Abfrage Distinct wie unten angegeben hinzu.

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
0
Manoj Kargeti