表:

UserId, Value, Date.

我想获得UserId,为每个UserId的最大值(日期)的值。也就是说,具有最新日期的每个UserId的值。有没有一种方法可以在SQL中简单地做到这一点?(最好是Oracle)

更新:为任何歧义道歉:我需要得到所有的用户id。但是对于每个UserId,只有该用户拥有最新日期的行。


当前回答

这也会处理重复的数据(为每个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

其他回答

(T-SQL)首先获取所有用户及其最大日期。与表连接以查找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

我没有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/55和http://www.akadia.com/services/ora_analytic_functions.html。这是一个简短的总结。

在底层,分析查询对整个数据集进行排序,然后按顺序进行处理。当你处理它的时候,你根据特定的标准对数据集进行分区,然后对每一行查看一些窗口(默认为当前行分区中的第一个值-默认也是最有效的),并可以使用许多分析函数(其列表非常类似于聚合函数)计算值。

在本例中,下面是内部查询的功能。整个数据集先按UserId排序,再按Date DESC排序,然后一次处理。对于每一行,您返回UserId和该UserId看到的第一个日期(因为日期是按DESC排序的,所以这是最大日期)。这就得到了重复行的答案。然后外部的DISTINCT压缩重复项。

这并不是一个特别引人注目的分析查询示例。如果想获得更大的胜利,可以考虑制作一张财务收据表格,计算每个用户和收据的总花费。分析查询可以有效地解决这个问题。其他解决方案效率较低。这就是为什么它们是2003 SQL标准的一部分。(不幸的是Postgres还没有。叽阿…)

如果(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;

答案是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)

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

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

希望这能对(任何人)有所帮助。:)

问候 古斯

由于不工作,我手头没有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

所以它是有效的,尽管其他地方提到的一些新东西可能性能更好。