表:

UserId, Value, Date.

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

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


当前回答

难道一个qualified子句不是既简单又最好吗?

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

对于上下文,在Teradata这里一个像样的大小测试运行在17秒与这个合格版本和在23秒与“内联视图”/Aldridge解决方案#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)

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

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

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

问候 古斯

我想是这样的。(请原谅我的语法错误;在这一点上,我习惯使用HQL !)

编辑:也误解了问题!修正了查询…

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

我看到许多人使用子查询或其他窗口函数来执行此操作,但我经常以以下方式执行这种不带子查询的查询。它使用普通的标准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和更大Date的行。

(我把标识符“Date”放在分隔符中,因为它是一个SQL保留字。)

如果是t1。Date = t2。“日期”,加倍出现。通常表有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的行,并对t2的所有列使用NULL作为占位符。这就是外层连接的工作原理。

此查询中的技巧是设计联接的匹配条件,使t2必须匹配相同的用户id和更大的日期。它的意思是,如果t2中存在一个日期更大的行,那么t1中与它比较的行就不能是该userid的最大日期。但是如果没有匹配——也就是说,如果t2中不存在比t1中的行日期大的行——我们就知道t1中的行是给定userid中日期最大的行。

在这些情况下(当没有匹配时),t2的列将为NULL——即使是连接条件中指定的列。这就是为什么我们用WHERE t2。UserId IS NULL,因为我们正在搜索没有为给定的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

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

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

这段代码运行良好的原因是它只需要扫描表一次。它不需要任何索引,最重要的是,它不需要像大多数分析函数那样对表进行排序。如果您需要为单个用户id过滤结果,索引将有所帮助。