Frage

Table:

UserId, Value, Date.

Ich mag den Benutzer-ID, Wert für den max (Datum) für jede Benutzer-ID bekommen. Das heißt, der Wert für jede Benutzer-ID, die das letzte Datum hat. Gibt es eine Möglichkeit, diese einfach in SQL zu tun? (Vorzugsweise Oracle)

Update: Wir entschuldigen uns für jede Zweideutigkeit: Ich brauche ALLE UserIds zu bekommen. Aber für jede Benutzer-ID, nur diese Zeile, wo der Benutzer das letzte Datum hat.

War es hilfreich?

Lösung

Damit wird alle Zeilen, für das Abrufen des my_date Spalt Wert auf den Maximalwert von my_date für diesen Benutzer-ID entspricht. Dies kann mehrere Zeilen für die Benutzer-ID abgerufen werden, wo das maximale Datum auf mehreren Zeilen ist.

select userid,
       my_date,
       ...
from
(
select userid,
       my_date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"Analytische Funktionen rock"

Edit: Im Hinblick auf den ersten Kommentar ...

„unter Verwendung von analytischer Abfragen und eine Selbstverknüpfung Niederlagen den Zweck der analytischen Abfragen“

Es gibt keine Selbstverknüpfung in diesem Code. Es ist vielmehr ein Prädikat auf dem Ergebnis der Inline-Ansicht platziert, die die analytische Funktion enthält -. Eine ganz andere Sache, und ganz gängige Praxis

„Das Standardfenster in Oracle ist von der ersten Zeile in der Partition des aktuellen“

Die Windowing-Klausel gilt nur in Gegenwart der Bestellung durch Klausel. Ohne ORDER BY-Klausel wird keine Windowing-Klausel standardmäßig angewendet und niemand kann explizit angegeben werden.

Der Code funktioniert.

Andere Tipps

Ich sehe viele Menschen Subqueries oder auch herstellerspezifische Funktionen nutzen, dies zu tun, aber ich habe oft diese Art von Abfrage ohne Unterabfragen in der folgenden Weise. Es verwendet einfache, SQL-Standard, so dass es in jeder Marke von RDBMS funktionieren soll.

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

Mit anderen Worten:. Holt die Zeilen aus t1, wenn keine anderen Zeile existiert mit dem gleichen UserId und ein größeren Datum

(habe ich die Kennung "Date" in Trennzeichen, da es sich um ein SQL reserviertes Wort ist.)

Im Fall, wenn t1."Date" = t2."Date" scheint verdoppelt. Normalerweise Tabellen hat auto_inc(seq) Schlüssel, z.B. id. Um zu vermeiden, Verdoppelung kann verwendet werden, folgt:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

Re Kommentar von @Farhan:

Hier ist eine ausführlichere Erklärung:

Eine äußere Verknüpfung Versuche t1 mit t2 beizutreten. Standardmäßig werden alle Ergebnisse von t1 zurückgegeben und , wenn eine Übereinstimmung in t2 ist, wird es auch zurückgegeben. Wenn es keine Übereinstimmung in t2 für eine gegebene Reihe von t1 ist, dann gibt die Abfrage noch die Reihe von t1 und verwendet NULL als Platzhalter für alle t2 Spalten. Das ist nur, wie äußere Arbeit im Allgemeinen verbindet.

Der Trick bei dieser Abfrage ist, um die Anpassungsbedingung der Join zu gestalten, dass t2 muß übereinstimmen gleiche userid und ein mehr date. Die Idee ist, wenn eine Zeile in t2 existiert, die eine größere date hat, dann die Zeile in t1 es im Vergleich ist gegen nicht der größte date für diesen userid sein. Aber wenn es keine Übereinstimmung gibt - das heißt, wenn keine Zeile mit einer größeren t2 als die Reihe in date in t1 existiert - wissen wir, dass die Reihe in t1 die Zeile mit dem größten date für die gegebenen userid war

.

In den Fällen (wenn es keine Übereinstimmung ist), werden die Spalten von t2 NULL werden - auch die Spalten in der Join-Bedingung angegeben. Also, warum wir WHERE t2.UserId IS NULL verwenden, weil wir für die Fälle, wo sind momentan keine Zeile mit einem größeren date für die gegebene userid gefunden wurde.

SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

Ich weiß nicht, Ihre genauen Spaltennamen, aber es wäre so etwas wie dies:

    select userid, value
      from users u1
     where date = (select max(date)
                     from users u2
                    where u1.userid = u2.userid)

Nicht bei der Arbeit zu sein, ich habe nicht Oracle zur Hand, aber ich glaube, daran zu erinnern, dass Oracle mehr Spalten gelegt werden, in einer IN-Klausel ermöglicht es, die die Optionen zumindest vermeiden sollten, die eine korrelierte Unterabfrage verwenden, das ist, selten eine gute Idee.

So etwas, vielleicht (kann mich nicht erinnern, wenn die Spaltenliste parenthesised werden soll oder nicht):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

EDIT: Gerade versucht es für real:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

So funktioniert es, auch wenn einige der neuen fangly Sachen an anderer Stelle erwähnt kann mehr performant.

Ich weiß, dass Sie sich für Oracle gefragt, aber in SQL 2005 haben wir diese jetzt verwenden:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

Wäre es nicht eine QUALIFY Klausel sowohl einfachste und am besten?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

Für Kontext, auf Teradata hier einen anständige Größe Test dieser läuft in 17s mit dieser Version QUALIFY und in 23s mit dem 'Inline-View' / Aldridge Lösung # 1.

Ich habe keine Oracle zu testen, aber die effizienteste Lösung ist analytische Abfragen verwendet werden. Es soll wie folgt aussehen:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

Ich vermute, dass Sie loswerden der äußeren Abfrage erhalten und auf der inneren verschieden gesetzt, aber ich bin mir nicht sicher. In der Zwischenzeit weiß, dass ich diese funktioniert.

Wenn Sie über analytische Abfragen lernen, ich würde vorschlagen, dass das Lesen http: //www.orafaq .com / node / 55 und http: //www.akadia. com / services / ora_analytic_functions.html . Hier ist die kurze Zusammenfassung.

Unter der Haube analytische Abfragen, um die gesamte Datensatz sortieren, sie dann nacheinander verarbeiten. Wie Sie es verarbeiten partitionieren Sie den Datensatz nach bestimmten Kriterien, und dann für jede Zeile zu einem bestimmten Fenster schaut (standardmäßig auf den ersten Wert in der Partition auf die aktuelle Zeile - das Standard ist auch die effizienteste) und Werte berechnen kann ein mit Anzahl der analytischen Funktionen (die Liste, von denen sehr ähnlich, die Aggregatfunktionen).

In diesem Fall hier ist das, was die innere Abfrage der Fall ist. Die gesamte Datensatz wird von UserId dann sortiert Datum DESC. Dann verarbeitet sie es in einem Durchgang. Für jede Zeile kehren Sie den Benutzer-ID und das erste Datum für die Benutzer-ID gesehen (seit Daten DESC sortiert, das ist das höchste Datum). Dadurch erhalten Sie Ihre Antwort mit duplizierten Zeilen. Dann werden die äußeren DISTINCT squashes Duplikate.

Dies ist kein besonders spektakuläres Beispiel für analytische Abfragen. Für einen viel größeren betrachtet Sieg eine Tabelle der Finanzeinnahmen zu nehmen und für jeden Benutzer und Erhalt der Berechnung einer laufenden Summe das, was sie bezahlte. Analytic Abfragen löst effektiv das. Andere Lösungen sind weniger effizient. Deshalb ist sie ein Teil der 2003 SQL-Standard sind. (Leider Postgres hat sie noch nicht haben. Grrr ...)

Oracle 12c+ , können Sie Top n Abfragen zusammen mit analytischer Funktion rank dies zu erreichen, sehr prägnant ohne Unterabfragen:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

Die oben genannten Erträge werden alle Zeilen mit max my_date pro Benutzer.

Wenn Sie nur eine Zeile mit max Datum wollen, dann ersetzen Sie die rank mit row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

Mit PostgreSQL 8.4 oder höher verwenden, können Sie diese verwenden:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

Verwenden Sie ROW_NUMBER() eine eindeutige Rangfolge zuweisen auf absteigend Date für jedes UserId, filtriere dann zu der ersten Zeile für jeden UserId (dh ROW_NUMBER = 1).

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

Sie hatte gerade ein "live" Beispiel bei der Arbeit zu schreiben:)

Dieses unterstützt mehrere Werte für Benutzer-ID auf der Seite gleichen Datum.

Spalten: UserId, Wert, Datum

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

Sie können FIRST_VALUE anstelle von MAX und es in dem Plan erklären nachschlagen. Ich habe nicht die Zeit, mit ihm zu spielen haben.

Natürlich, wenn die Suche durch riesige Tabellen, ist es wahrscheinlich besser, wenn Sie FULL Hinweise in Ihrer Abfrage verwenden.

select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

Ich denke, so etwas wie diese. (Verzeihen Sie mir für alle Syntaxfehler;! Ich mit HQL an dieser Stelle verwendet bin)

EDIT: Auch die Frage falsch verstanden! Korrigiert die Abfrage ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

i Sache sollten Sie diese Variante zur vorherige Abfrage machen:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

(T-SQL) erhalten zunächst alle Benutzer und deren MaxDate. Join mit der Tabelle die entsprechenden Werte für die Benutzer in dem maxdates zu finden.

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

Ergebnisse:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

Die Antwort ist nur Oracle. Hier ist ein bisschen anspruchsvollere Antwort in jedem SQL:

Wer hat die beste Gesamthausaufgaben Ergebnis (maximale Summe von Hausaufgaben Punkte) hat?

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

Und ein schwierigeres Beispiel, die eine Erklärung benötigen, für die ich Zeit atm nicht:

Geben Sie das Buch (ISBN und Titel), die im Jahr 2008 am beliebtesten ist, das heißt, die im Jahr 2008 am häufigsten ausgeliehen wird.

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

Hope, das hilft (jedermann) ..:)

Viele Grüße, Guus

Unter der Annahme Datum für eine bestimmte Benutzer-ID eindeutig zuzuordnen ist, hier einige TSQL:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

Ich bin ziemlich spät, um die Partei, sondern die folgenden Hack wird beide korrelierte Unterabfragen und jede Analysefunktion übertreffen, hat aber eine Einschränkung: Werte in Strings konvertieren müssen. So funktioniert es für Daten, Zahlen und andere Saiten. Der Code sieht nicht gut aus, aber das Ausführungsprofil ist groß.

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

Der Grund, warum dieser Code funktioniert so gut ist, dass es nur einmal um die Tabelle scannen muss. Es erfordert keine Indizes und vor allem braucht es nicht um die Tabelle zu sortieren, die die meisten Analysefunktionen tun. Die Indizes werden jedoch helfen, wenn Sie das Ergebnis für eine einzelne Benutzer-ID filtern müssen.

select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

IMHO das funktioniert. HTH

Ich denke, das sollte funktionieren?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

Zuerst versuche ich die Frage falsch verstanden, nach der Top-Antwort, hier ist ein komplettes Beispiel mit dem richtigen Ergebnissen:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

-

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

-

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

Dies wird auch Pflege von Duplikaten (Rückkehr eine Zeile für jede user_id):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

getestet Gerade dies und es scheint, auf einer Logging-Tabelle zu arbeiten

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

Dies sollte so einfach sein wie:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

Wenn Sie Postgres verwenden, können Sie wie

verwenden array_agg
SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

Ich bin nicht vertraut mit Oracle. Das ist, was ich kam mit

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

Beide Abfragen geben die gleichen Ergebnisse wie die akzeptierte Antwort. Siehe SQLFiddles:

  1. akzeptierte Antwort
  2. Meine Lösung mit Postgres
  3. Meine Lösung mit Oracle

Wenn (UserID, Datum) einzigartig ist, das heißt kein Datum zweimal für den gleichen Benutzer erscheint dann:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;
select   UserId,max(Date) over (partition by UserId) value from users;

Lösung für MySQL, die nicht über Konzepte der Partition HALTEN, DENSE_RANK.

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

Referenz: http: // benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top