626

桌子:

UserId, Value, Date.

我想获取每个 UserId 的 Max(Date) 的 UserId 值。即,每个具有最新日期的 UserId 的值。有没有办法简单地在 SQL 中做到这一点?(最好是甲骨文)

更新:对任何歧义道歉:我需要获取所有用户 ID。但是对于每个 UserId,只有该用户具有最新日期的那一行。

4

35 回答 35

480

我看到很多人使用子查询或其他窗口函数来执行此操作,但我经常通过以下方式进行这种没有子查询的查询。它使用普通的标准 SQL,因此它可以在任何品牌的 RDBMS 中工作。

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;

换句话说:t1从不存在具有相同UserId和更大日期的其他行的行中获取行。

(我将标识符“日期”放在分隔符中,因为它是 SQL 保留字。)

如果t1."Date" = t2."Date",则出现加倍。通常表有auto_inc(seq)键,例如id。为了避免加倍可以使用如下:

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;

来自@Farhan 的重新评论:

这里有一个更详细的解释:

外部联接尝试与t1联接t2。默认情况下,返回 的所有结果t1如果在 中匹配t2,也返回。t2如果的给定行没有匹配t1,则查询仍返回 的行t1,并NULL用作所有t2列的占位符。这就是外部连接的一般工作方式。

这个查询的技巧是设计连接的匹配条件,使得t2必须匹配相同 userid的和更大的 date。这个想法是,如果其中存在t2一个具有更大date的行,那么t1与之比较的行就不可能是最大dateuserid。但是如果没有匹配项——即,如果不存在t2大于date行中的行t1——我们知道中的行是给定t1的最大的行。dateuserid

在这些情况下(当不匹配时), 的列t2将是NULL-- 甚至是连接条件中指定的列。所以这就是我们使用 的原因WHERE t2.UserId IS NULL,因为我们正在搜索未找到行的情况,date而给定的 更大userid

于 2008-09-23T20:01:21.937 回答
433

这将检索 my_date 列值等于该用户标识的 my_date 最大值的所有行。这可能会为用户标识检索多行,其中最大日期在多行上。

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

“分析函数摇滚”

编辑:关于第一条评论......

“使用分析查询和自联接违背了分析查询的目的”

此代码中没有自联接。相反,在包含分析函数的内联视图的结果上放置了一个谓词——这是一个非常不同的问题,并且是完全标准的做法。

“Oracle 中的默认窗口是从分区中的第一行到当前行”

windowing 子句仅适用于存在 order by 子句的情况。如果没有 order by 子句,默认情况下不应用任何窗口子句,也不能显式指定。

该代码有效。

于 2008-09-23T14:41:11.677 回答
168
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid
于 2008-09-23T15:18:24.830 回答
55

我不知道你的确切列名,但它会是这样的:

    选择用户名,值
      来自用户 u1
     其中日期 =(选择最大值(日期)
                     来自用户 u2
                    其中 u1.userid = u2.userid)
于 2008-09-23T14:39:13.007 回答
43

不在工作,我手头没有 Oracle,但我似乎记得 Oracle 允许在 IN 子句中匹配多个列,这至少应该避免使用相关子查询的选项,这很少是好的主意。

可能是这样的(不记得列列表是否应该加括号):

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

编辑:刚刚尝试过:

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

所以它起作用了,尽管其他地方提到的一些新奇的东西可能性能更高。

于 2008-09-23T20:06:29.973 回答
14

我知道您要求使用 Oracle,但在 SQL 2005 中,我们现在使用它:


-- 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
于 2008-09-23T15:22:48.410 回答
8

我没有 Oracle 来测试它,但最有效的解决方案是使用分析查询。它应该看起来像这样:

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

我怀疑您可以摆脱外部查询并将 distinct 放在内部,但我不确定。与此同时,我知道这个有效。

如果您想了解分析查询,我建议您阅读http://www.orafaq.com/node/55http://www.akadia.com/services/ora_analytic_functions.html。这是简短的摘要。

在后台,分析查询对整个数据集进行排序,然后按顺序对其进行处理。在处理它时,您根据某些标准对数据集进行分区,然后为每一行查看某个窗口(默认为当前行的分区中的第一个值 - 该默认值也是最有效的)并且可以使用分析函数的数量(其列表与聚合函数非常相似)。

在这种情况下,这是内部查询的作用。整个数据集按 UserId 然后 Date DESC 排序。然后它一次性处理它。对于每一行,您返回 UserId 和为该 UserId 看到的第一个日期(因为日期按 DESC 排序,这是最大日期)。这为您提供了重复行的答案。然后外部 DISTINCT 挤压重复项。

这不是一个特别引人注目的分析查询示例。为了获得更大的胜利,请考虑使用一张财务收据表,并为每个用户和收据计算他们支付的总金额。分析查询有效地解决了这个问题。其他解决方案效率较低。这就是为什么它们是 2003 SQL 标准的一部分。(不幸的是 Postgres 还没有它们。Grrr ...)

于 2008-09-23T15:47:54.073 回答
7

QUALIFY 子句不是既简单又最好?

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

就上下文而言,在 Teradata 上,这个 QUALIFY 版本在 17 秒内运行,在 23 秒内运行 'inline view'/Aldridge 解决方案 #1。

于 2011-10-19T16:17:08.583 回答
7

使用 PostgreSQL 8.4 或更高版本,您可以使用:

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
于 2011-11-01T13:22:23.680 回答
7

Oracle 12c+中,您可以使用Top n查询和分析函数rank来非常简洁地实现这一点,而无需子查询:

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

以上返回每个用户最大 my_date 的所有行。

如果您只想要一行具有最大日期,则将其替换rankrow_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; 
于 2017-03-26T12:07:40.033 回答
5

用于为 eachROW_NUMBER()分配一个唯一的降序排名,然后过滤到每个的第一行(即= 1)。DateUserIdUserIdROW_NUMBER

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;
于 2017-10-31T13:07:35.730 回答
4

只需要在工作中写一个“活”的例子:)

这个支持同一日期的 UserId 的多个值。

列:用户 ID、值、日期

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
)

您可以使用 FIRST_VALUE 代替 MAX 并在说明计划中查找。我没有时间玩它。

当然,如果搜索巨大的表,最好在查询中使用 FULL 提示。

于 2010-06-29T13:45:48.477 回答
4

我参加聚会已经很晚了,但是以下 hack 将优于相关子查询和任何分析功能,但有一个限制:值必须转换为字符串。所以它适用于日期、数字和其他字符串。代码看起来不太好,但执行配置文件很棒。

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

这段代码之所以这么好,是因为它只需要扫描表一次。它不需要任何索引,最重要的是它不需要对表进行排序,而大多数分析功能都需要这样做。如果您需要过滤单个用户标识的结果,索引将有所帮助。

于 2014-07-21T08:27:20.720 回答
3

如果你使用 Postgres,你可以使用array_agglike

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

我对甲骨文不熟悉。这就是我想出的

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 

两个查询都返回与接受的答案相同的结果。请参阅 SQLFiddles:

  1. 接受的答案
  2. 我使用 Postgres 的解决方案
  3. 我使用 Oracle 的解决方案
于 2014-11-11T18:53:58.947 回答
2

我想是这样的。(请原谅我的任何语法错误;我现在已经习惯使用 HQL!)

编辑:也误读了这个问题!更正了查询...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)
于 2008-09-23T14:36:59.923 回答
2

我觉得你应该对前面的查询做这个变体:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)
于 2008-09-23T14:47:21.100 回答
2
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  
于 2008-09-23T14:51:02.777 回答
2
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
于 2011-11-23T13:47:44.890 回答
1

(T-SQL) 首先获取所有用户及其 maxdate。加入表格以查找用户在 maxdates 上的对应值。

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

结果:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000
于 2008-09-23T14:39:31.850 回答
1

假设 Date 对于给定的 UserID 是唯一的,这里有一些 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 
于 2008-09-23T14:49:33.840 回答
1

这里的答案只有 Oracle。这是所有 SQL 中更复杂的答案:

谁的整体功课成绩最好(功课总分最高)?

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)

还有一个更困难的例子,需要一些解释,我没有时间atm:

给出 2008 年最流行的书(ISBN 和书名),即 2008 年最常被借阅的书。

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

希望这有助于(任何人).. :)

问候,古斯

于 2010-04-28T17:04:23.813 回答
1

刚刚对此进行了测试,它似乎可以在日志表上运行

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc
于 2010-05-02T15:12:43.197 回答
1

没有分区 KEEP、DENSE_RANK 概念的 MySQL 解决方案。

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

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

于 2013-08-30T18:36:07.783 回答
0
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

恕我直言,这有效。高温高压

于 2008-09-23T14:57:43.523 回答
0

我认为这应该工作?

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
于 2008-09-23T15:05:01.727 回答
0

这应该很简单:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)
于 2008-09-23T15:11:04.083 回答
0

首先尝试我误读了问题,按照最佳答案,这是一个具有正确结果的完整示例:

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)
于 2008-09-23T15:17:59.950 回答
0

这也将处理重复(为每个 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
于 2010-02-24T17:07:28.487 回答
0
select   UserId,max(Date) over (partition by UserId) value from users;
于 2013-04-21T02:36:36.990 回答
-1

如果 (UserID, Date) 是唯一的,即同一用户没有出现两次日期,则:

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;
于 2008-09-23T14:44:53.087 回答
-1

如果您的问题与该页面相似,请检查此链接,那么我建议您使用以下查询,该查询将为该链接提供解决方案

select distinct sno,item_name,max(start_date) over(partition by sno),max(end_date) over(partition by sno),max(creation_date) over(partition by sno), max(last_modified_date) over(partition by sno) from uniq_select_records order by sno,item_name asc;

将给出与该链接相关的准确结果

于 2015-06-17T10:09:08.490 回答
-1

使用代码:

select T.UserId,T.dt from (select UserId,max(dt) 
over (partition by UserId) as dt from t_users)T where T.dt=dt;

这将检索结果,而不考虑 UserId 的重复值。如果您的 UserId 是唯一的,那么它会变得更加简单:

select UserId,max(dt) from t_users group by UserId;
于 2017-05-11T11:18:22.907 回答
-1
SELECT a.userid,a.values1,b.mm 
FROM table_name a,(SELECT userid,Max(date1)AS mm FROM table_name GROUP BY userid) b
WHERE a.userid=b.userid AND a.DATE1=b.mm;
于 2018-10-16T07:08:07.223 回答
-1

下面的查询可以工作:

SELECT user_id, value, date , row_number() OVER (PARTITION BY user_id ORDER BY date desc) AS rn
FROM table_name
WHERE rn= 1
于 2020-05-14T10:58:07.897 回答
-2
SELECT a.* 
FROM user a INNER JOIN (SELECT userid,Max(date) AS date12 FROM user1 GROUP BY userid) b  
ON a.date=b.date12 AND a.userid=b.userid ORDER BY a.userid;
于 2017-09-08T09:50:37.407 回答