Wie frage ich SQL nach einem aktuellen Aufnahmedatum für jeden Benutzer ab?


228

Ich habe eine Tabelle, die eine Sammlung von Einträgen darüber enthält, wann ein Benutzer angemeldet war.

username, date,      value
--------------------------
brad,     1/2/2010,  1.1
fred,     1/3/2010,  1.0
bob,      8/4/2009,  1.5
brad,     2/2/2010,  1.2
fred,     12/2/2009, 1.3

etc..

Wie erstelle ich eine Abfrage, die mir das neueste Datum für jeden Benutzer gibt?

Update: Ich habe vergessen, dass ich einen Wert haben muss, der mit dem letzten Datum übereinstimmt.


7
Welche Datenbank verwenden Sie? MySQL, SQL-Server, Oracle, ...?
Peter Lang

1
Benötigen Sie den Wert, der zum letzten Datum passt, oder den Maximalwert UND das Maximaldatum?
Matthew Jones

Antworten:


381
select t.username, t.date, t.value
from MyTable t
inner join (
    select username, max(date) as MaxDate
    from MyTable
    group by username
) tm on t.username = tm.username and t.date = tm.MaxDate

3
Wäre diese Version bei der Arbeit mit postgresql schneller als die Verwendung einer IN (Unterabfrage) anstelle des inneren Joins?
TheOne

3
@ TheOne als meine Erfahrung, ist die Verwendung von Inner Join schneller als in Zustand
Dada

13
Vorsicht bei diesem Ansatz: Es kann mehr als eine Zeile pro Benutzer zurückgeben, wenn mehr als ein Datensatz pro Datum vorhanden ist ( max(date)würde ein Datum zurückgeben, das mehrere Datensätze verbinden würde). Um dieses Problem zu vermeiden, ist es vorzuziehen, die Lösung von @ dotjoe zu verwenden: stackoverflow.com/a/2411763/4406793 .
Marco Roy

@ RedFilter Das hat perfekt für mein Problem funktioniert. Vielen Dank für eine solche technische Anfrage. Übrigens habe ich datetime anstelle von date verwendet, um zu vermeiden, dass für ein bestimmtes Datum mehrere Ergebnisse erzielt werden
Muhammad Khan,

Warum brauchen Sie das 'und t.date = tm.MaxDate', würde die Gruppierung nicht ausreichen?
Duldi

124

Verwenden von Fensterfunktionen (funktioniert in Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)

select * from (
    select
        username,
        date,
        value,
        row_number() over(partition by username order by date desc) as rn
    from
        yourtable
) t
where t.rn = 1

1
Es lohnt sich zu klären, welches Sybase-Produkt / welche Sybase-Version. Es funktioniert nicht auf Sybase ASE 16.
Levant pied

2
Ein großer Vorteil dieses Ansatzes besteht darin, dass garantiert immer nur eine Zeile pro Partition zurückgegeben wird ( usernamein diesem Fall) und nicht einmal ein eindeutiges "bestellbares" Feld erforderlich ist (wie das Mitmachen max(date)in anderen Antworten).
Marco Roy

1
Nur um etwas zu dem hinzuzufügen, was @MarcoRoy gesagt hat: Wenn Sie zufällig mehr als einen Datensatz mit demselben maximalen Datum haben, wenn Sie die Abfrage ändern, wie beim Debuggen, erhält ein anderer Datensatz möglicherweise die Zeilennummer 1 Die Ergebnisse können inkonsistent sein. Aber solange es dich wirklich nicht interessiert, sollte dies kein Problem sein. Dies kann behoben werden, wenn Sie die PK nach dem Datum hinzufügen. Zum Beispiel : order by date desc, id desc).
Andrew

40

Ich sehe, dass die meisten Entwickler eine Inline-Abfrage verwenden, ohne die Auswirkungen auf große Datenmengen zu berücksichtigen.

Sie können dies einfach erreichen, indem Sie:

SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username 
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;

3
Tatsächlich funktioniert dies nur für Duplikate. Wenn Sie mehr als 2 Werte haben, funktioniert die Bedingung a.date <b.date nicht, was bedeutet, dass es keine allgemeine Lösung ist, obwohl die Idee, mit dem LEFT OUTER JOIN zu arbeiten, wichtig ist Sache in dieser Antwort.
iversoncru

Interessanterweise funktioniert Sybase ASE 16 gut für kleinere (<10.000 Zeilen) Tabellen, aber bei größeren (> 100.000 Zeilen) ist es ein Problem ... Ich dachte, dies wäre das perfekte Beispiel dafür, dass relationale DBs sich durch ...
Levant Pied

1
@levantpied ... Ja, Linksverknüpfung ist bei größeren Datensätzen teuer. Sie können eine Leistung optimieren, indem Sie die Filterbedingung auf Join selbst setzen, um sie nach Möglichkeit auf irgendeine Weise zu handhaben.
Sujeet

20

So erhalten Sie die gesamte Zeile mit dem maximalen Datum für den Benutzer:

select username, date, value
from tablename where (username, date) in (
    select username, max(date) as date
    from tablename
    group by username
)

1
Arbeiten für MySQL
School Boy

1
Beachten Sie, dass Sie dadurch Duplikate erhalten, wenn für einen bestimmten Benutzer mehr als ein Datensatz mit demselben Datum vorhanden ist. Sie können dies wollen oder nicht.
Andrew

Diese SQL ist in Oracle langsam mit in Klausel, es wird nicht den Index verwenden
Meadlai

9
SELECT *     
FROM MyTable T1    
WHERE date = (
   SELECT max(date)
   FROM MyTable T2
   WHERE T1.username=T2.username
)

4
Dies ist zwar eine weitere mögliche Lösung, aber normalerweise kein guter Weg, um dies zu lösen. Wenn Sie dies auf diese Weise tun, wird die innere Abfrage für jeden Namen in der Tabelle einmal ausgeführt, was zu einer erheblichen Verlangsamung für jede Tabelle mit erheblicher Größe führt. Das Ausführen einer separaten Abfrage, die kein Element aus der ersten Abfrage in der where-Klausel enthält, und das Zusammenfügen der beiden Tabellen ist normalerweise schneller.
Scott Chamberlain

Dies hat die nette Eigenschaft, eine der verständlicheren Lösungen zu sein, die nicht implementierungsspezifisch sind.
Michael Szczepaniak

7

Nach meiner Erfahrung ist der schnellste Weg, jede Zeile zu nehmen, für die es keine neuere Zeile in der Tabelle gibt.

Ein weiterer Vorteil ist, dass die verwendete Syntax sehr einfach ist und die Bedeutung der Abfrage ziemlich leicht zu verstehen ist (nehmen Sie alle Zeilen so, dass für den betrachteten Benutzernamen keine neuere Zeile vorhanden ist).

EXISTIERT NICHT

SELECT username, value
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS witness
  WHERE witness.username = t.username AND witness.date > t.date
);

ZEILENNUMMER

SELECT username, value
FROM (
  SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
  FROM t
) t2
WHERE rn = 1

INNER JOIN

SELECT t.username, t.value
FROM t
INNER JOIN (
  SELECT username, MAX(date) AS date
  FROM t
  GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;

LINKE ÄUSSERE VERBINDUNG

SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL

Ich habe Schwierigkeiten, die NOT EXISTS-Version zu verstehen. Fehlt Ihnen nicht eine Aggregation im Unterabfrageteil? Wenn ich dies auf meinem Tisch ausführe, erhalte ich nur 3 Mitarbeiterdatensätze von 40 Mitarbeitern zurück, die ich in der Tabelle habe. Ich sollte mindestens 40 Platten bekommen. Sollten wir in der inneren Abfrage nicht auch nach Benutzername übereinstimmen?
Narshe

Es funktioniert für mich mit den folgenden:SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date > t.date AND witness.username = t.username );
Narshe

Ich habe mir NOT NOT EXISTS angesehen und es wird nur der höhere Eintrag für alle Benutzer zurückgegeben, im Gegensatz zu: "Eine Abfrage, die mir das neueste Datum für jeden Benutzer geben würde".
Tasos Zervos

Sie haben in der Tat Recht, ich aktualisiere meine Anfrage. Vielen Dank für Ihre Bemerkung! @Narshe Entschuldigung, ich habe deine Kommentare aus irgendeinem Grund verpasst: / Aber du hast absolut Recht.
Fabian Pijcke

2

Dieser sollte Ihnen das richtige Ergebnis für Ihre bearbeitete Frage liefern.

Die Unterabfrage stellt sicher, dass nur Zeilen mit dem letzten Datum gefunden werden, und die äußere Abfrage GROUP BYkümmert sich um die Verknüpfungen. Wenn zwei Einträge für dasselbe Datum für denselben Benutzer vorhanden sind, wird der Eintrag mit dem höchsten zurückgegeben value.

SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
       SELECT username, MAX( date ) date
       FROM your_table
       GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date

1

Sie können auch die analytische Rangfunktion verwenden

    with temp as 
(
select username, date, RANK() over (partition by username order by date desc) as rnk from t
)
select username, rnk from t where rnk = 1

0
SELECT Username, date, value
 from MyTable mt
 inner join (select username, max(date) date
              from MyTable
              group by username) sub
  on sub.username = mt.username
   and sub.date = mt.date

Würde das aktualisierte Problem beheben. Bei großen Tabellen funktioniert dies möglicherweise nicht so gut, selbst bei guter Indizierung.


0
SELECT *
FROM ReportStatus c
inner join ( SELECT 
  MAX(Date) AS MaxDate
  FROM ReportStatus ) m
on  c.date = m.maxdate

0

Bei Oracle sortiert die Ergebnismenge in absteigender Reihenfolge und nimmt den ersten Datensatz, sodass Sie den neuesten Datensatz erhalten:

select * from mytable
where rownum = 1
order by date desc

0
SELECT DISTINCT Username, Dates,value 
FROM TableName
WHERE  Dates IN (SELECT  MAX(Dates) FROM TableName GROUP BY Username)


Username    Dates       value
bob         2010-02-02  1.2       
brad        2010-01-02  1.1       
fred        2010-01-03  1.0       

Dies würde wahrscheinlich nicht funktionieren, wenn mehrere Benutzer Bestellungen am selben Datum hätten. Was wäre, wenn Brad und Bob beide am 2. Januar eine Bestellung hätten?
AHiggins

Ich gruppiere nach Benutzername, damit es funktioniert und die Ergebnisse folgendermaßen aussehen: Benutzername Datum Wert Bob 2010-02-02 1.2 Brad 2010-02-02 1.4 Fred 2010-01-03 1.0
Wara

0
SELECT t1.username, t1.date, value
FROM MyTable as t1
INNER JOIN (SELECT username, MAX(date)
            FROM MyTable
            GROUP BY username) as t2 ON  t2.username = t1.username AND t2.date = t1.date

4
Ein oder zwei Sätze zur Implementierung oder Erklärung tragen wesentlich dazu bei, eine qualitativ hochwertige Antwort zu erstellen.

0

Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)

Die innere Abfrage gibt das späteste Datum für den aktuellen Benutzer zurück. Die äußere Abfrage ruft alle Daten gemäß dem Ergebnis der inneren Abfrage ab.


0

Auf diese Weise habe ich den letzten Datensatz für jeden Benutzer erstellt, den ich auf meinem Tisch habe. Es war eine Abfrage, um den letzten Standort für den Verkäufer zu ermitteln, der zuletzt auf PDA-Geräten ermittelt wurde.

CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE()) 
Group By GS.UserID
GO
select  gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
        from USERGPS gs
        inner join USER s on gs.SalesManNo = s.SalesmanNo 
        inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate 
        order by LastDate desc

0
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)

Willkommen bei StackOverflow und vielen Dank, dass Sie versucht haben zu helfen. Nur-Code-Antworten wie Ihre werden im Vergleich zu Antworten, die die Lösung erklären, weniger geschätzt.
Yunnosch

Bitte lesen Sie diese Anleitung, um eine qualitativ hochwertige Antwort zu erhalten.
thewaywewere

und. Es wird nicht für jeden Benutzernamen zu MAX zurückgekehrt, sondern nur zur letzten einzelnen Zeile.
IrvineCAGuy

0

Meine kleine Zusammenstellung

  • Selbst joinbesser als verschachteltselect
  • group bygibt dir aber nicht primary keywas für vorzuziehen istjoin
  • Dieser Schlüssel kann partition byin Verbindung mit first_value( docs ) angegeben werden.

Also, hier ist eine Abfrage:

wählen
 t. *
von 
 Tabelle t inner join (
  Wählen Sie als ID einen eindeutigen first_value (ID) über (Partition nach GroupColumn-Reihenfolge nach DateColumn desc)
  aus der Tabelle
  Dabei ist FilterColumn = 'Wert'
 ) j auf t.ID = j.ID.

Vorteile:

  • Filtern Sie Daten mit whereAnweisungen mithilfe einer beliebigen Spalte
  • select Alle Spalten aus gefilterten Zeilen

Nachteile:

  • Benötigen Sie MS SQL Server ab 2012.

0

Ich habe etwas für meine Bewerbung getan, da es:

Unten ist die Abfrage:

select distinct i.userId,i.statusCheck, l.userName from internetstatus 
as i inner join login as l on i.userID=l.userID 
where nowtime in((select max(nowtime) from InternetStatus group by userID));    

0

Dies ähnelt einer der obigen Antworten, ist aber meiner Meinung nach viel einfacher und aufgeräumter. Zeigt auch eine gute Verwendung für die Kreuzanwendungsanweisung. Für SQL Server 2005 und höher ...

select
    a.username,
    a.date,
    a.value,
from yourtable a
cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
where a.date=b.maxdate

0
SELECT MAX(DATE) AS dates 
FROM assignment  
JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
     paper_submission_detail.PAPER_SUB_ID 

1
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten. Aus der Überprüfung
doppelter Piepton

-2

Dies sollte auch funktionieren, um die neuesten Einträge für Benutzer zu erhalten.

SELECT username, MAX(date) as Date, value
FROM MyTable
GROUP BY username, value

1
Hallo, die Wertespalte muss in der group by-Klausel enthalten sein.
Juan Ruiz de Castilla

-4

Sie würden die Aggregatfunktion MAX und GROUP BY verwenden

SELECT username, MAX(date), value FROM tablename GROUP BY username, value

7
Bei Ihrer Bearbeitung wird nur ein Zufall ausgewählt value, nicht der mit der MAX(date)Zeile verknüpfte .
Alison R.

Es wird das maximale Datum angegeben, aber der Benutzername und der Wert stimmen möglicherweise nicht überein.
SKR
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.