表:
UserId, Value, Date.
我想获取UserId,每个UserId的max(Date)值。即,具有最新日期的每个UserId的值。有没有办法在SQL中简单地做到这一点?(最好是Oracle)
更新:对于任何歧义,我们深表歉意:我需要获取所有UserIds。但是对于每个UserId,仅该用户具有最新日期的那一行。
表:
UserId, Value, Date.
我想获取UserId,每个UserId的max(Date)值。即,具有最新日期的每个UserId的值。有没有办法在SQL中简单地做到这一点?(最好是Oracle)
更新:对于任何歧义,我们深表歉意:我需要获取所有UserIds。但是对于每个UserId,仅该用户具有最新日期的那一行。
Answers:
这将检索其my_date列值等于该用户ID的my_date最大值的所有行。这可能会为用户ID检索多行,其中最大日期在多行上。
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子句,则默认情况下不应用任何窗口条款,并且不能明确指定任何子句。
该代码有效。
MAX(...) OVER (...)
您也可以使用ROW_NUMBER() OVER (...)
(对于每组最高的n)或RANK() OVER (...)
(对于每组最大的n)。
我看到许多人使用子查询或其他特定于供应商的功能来执行此操作,但是我经常通过以下方式在没有子查询的情况下进行此类查询。它使用普通的标准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”放在分隔符中,因为它是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
它反对相比,不能是最大date
的那个userid
。但是,如果没有匹配项(即,如果不存在比输入的行t2
更大date
的行),t1
我们知道输入的行t1
是date
给定行最大的行userid
。
在这些情况下(如果没有匹配项),的列t2
将为NULL
-甚至是联接条件中指定的列。这就是我们之所以使用它的原因WHERE t2.UserId IS NULL
,因为我们正在寻找找不到date
给定更大行的情况userid
。
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
FROM table
GROUP BY userid
我不知道您确切的列名称,但是可能是这样的:
选择用户ID,值 来自用户u1 日期=(选择最大(日期) 来自用户u2 其中u1.userid = u2.userid)
没有工作,我没有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
这样就可以了,尽管其他地方提到的一些新事物可能更有效。
我知道您要求使用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
我没有Oracle对其进行测试,但是最有效的解决方案是使用解析查询。它看起来应该像这样:
SELECT DISTINCT
UserId
, MaxValue
FROM (
SELECT UserId
, FIRST (Value) Over (
PARTITION BY UserId
ORDER BY Date DESC
) MaxValue
FROM SomeTable
)
我怀疑您可以摆脱外部查询并将内部查询与众不同,但我不确定。同时,我知道这是可行的。
如果您想了解解析查询,建议阅读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还没有它们。Grrr...)
QUALIFY子句既不是最简单的又是最好的?
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1
就上下文而言,在此Teradata上使用此QUALIFY版本对此进行了不错的尺寸测试,对于“内联视图” / Aldridge解决方案#1在23s中进行了测试。
rank()
在有联系的情况下,请谨慎使用该功能。您可能会得到不止一个rank=1
。row_number()
如果您确实只希望返回一条记录,则最好使用。
QUALIFY
子句特定于Teradata。在Oracle中(至少),您必须使用WHERE
包装的select语句上的子句嵌套查询和过滤器(我想,这可能会降低性能)。
在中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的所有行。
如果你想只有一排,最大日期,然后更换rank
有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;
使用ROW_NUMBER()
分配唯一的排名上降序Date
对每个UserId
过滤器,然后到第一行的每个UserId
(即,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;
只需在工作中写一个“实时”示例:)
该日期在同一日期支持UserId的多个值。
列:UserId,值,日期
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提示。
我认为是这样的。(原谅我任何语法错误;这时我已经习惯了使用HQL!)
编辑:也误解了问题!更正了查询...
SELECT UserId, Value
FROM Users AS user
WHERE Date = (
SELECT MAX(Date)
FROM Users AS maxtest
WHERE maxtest.UserId = user.UserId
)
(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
答案只有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);
希望对大家有帮助。.::)
问候,Guus
我参加聚会的时间很晚,但是以下破解将胜过相关子查询和任何分析功能,但有一个限制:值必须转换为字符串。因此,它适用于日期,数字和其他字符串。代码看起来不好,但是执行配置文件很棒。
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的结果,索引将有所帮助。
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
恕我直言,这有效。高温超导
首先尝试我误解了问题,然后再回答最高答案,这是一个完整的示例,具有正确的结果:
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)
不具有分区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
如果你使用的是Postgres,你可以使用array_agg
像
SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid
我对Oracle不熟悉。这就是我想出的
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:
如果(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;