提问人:Umang 提问时间:9/23/2008 最后编辑:philipxyUmang 更新时间:5/13/2023 访问量:685325
对于另一列的每个非重复值,提取具有列的最大值的行
Fetch the rows which have the Max value for a column for each distinct value of another column
问:
桌子:
UserId, Value, Date.
我想获取 UserId,每个 UserId 的 max(Date) 值。也就是说,具有最新日期的每个 UserId 的值。
如何在 SQL 中执行此操作?(最好是 Oracle。
我需要获取所有UserId。但对于每个 UserId,只有该用户具有最新日期的那一行。
答:
我想是这样的。(请原谅我的任何语法错误;在这一点上,我已经习惯使用HQL!
编辑:也误读了这个问题!更正了查询...
SELECT UserId, Value
FROM Users AS user
WHERE Date = (
SELECT MAX(Date)
FROM Users AS maxtest
WHERE maxtest.UserId = user.UserId
)
评论
我不知道你的确切列名,但它是这样的:
SELECT userid, value
FROM users u1
WHERE date = (
SELECT MAX(date)
FROM users u2
WHERE u1.userid = u2.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
这将检索其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 中的默认窗口是从分区中的第一行到当前行”
窗口子句仅在存在 order by 子句的情况下适用。如果没有 order by 子句,则默认情况下不应用任何窗口子句,也不能显式指定任何子句。
代码有效。
评论
MAX(...) OVER (...)
ROW_NUMBER() OVER (...)
RANK() OVER (...)
如果 (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;
评论
我 Thing you shuold 将这个变体添加到之前的查询中:
SELECT UserId, Value FROM Users U1 WHERE
Date = ( SELECT MAX(Date) FROM Users where UserId = U1.UserId)
假设 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
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
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
恕我直言,这有效。HTH型
我认为这应该有效吗?
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
这应该像以下几点一样简单:
SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)
首先尝试我误读了问题,在顶部答案之后,这是一个具有正确结果的完整示例:
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)
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
FROM table
GROUP BY userid
评论
我知道你要求 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
)
我怀疑您可以摆脱外部查询并在内部放置distinct,但我不确定。与此同时,我知道这个有效。
如果你想了解分析查询,我建议你阅读 http://www.orafaq.com/node/55 和 http://www.akadia.com/services/ora_analytic_functions.html。这是简短的摘要。
在后台,分析查询对整个数据集进行排序,然后按顺序进行处理。在处理数据集时,根据某些条件对数据集进行分区,然后对于每一行,查看某个窗口(默认为分区中的第一个值到当前行 - 该默认值也是最有效的),并且可以使用许多分析函数(其列表与聚合函数非常相似)计算值。
在本例中,以下是内部查询的作用。整个数据集按 UserId 和 Date DESC 排序。然后它一次性处理它。对于每一行,返回 UserId 和该 UserId 的第一个 Date(因为日期按 DESC 排序,因此这是最大日期)。这为您提供了重复行的答案。然后外部 DISTINCT 压缩重复项。
这不是一个特别壮观的分析查询示例。为了获得更大的胜利,请考虑使用财务收据表并计算每个用户和收据的连续总额。分析查询可以有效地解决这个问题。其他解决方案的效率较低。这就是为什么它们是 2003 SQL 标准的一部分。(不幸的是,Postgres 还没有它们。
评论
我看到很多人使用子查询或其他窗口函数来做到这一点,但我经常用以下方式在没有子查询的情况下进行这种查询。它使用普通的标准 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
此查询的诀窍是设计连接的匹配条件,使其必须匹配相同的 和更大的 .这个想法是,如果其中存在一行具有更大的 ,那么与之比较的行就不可能是最大的。但是,如果没有匹配项,即如果 中不存在大于 in 的行,我们知道 in 中的行是给定的 的最大行。t2
userid
date
t2
date
t1
date
userid
t2
date
t1
t1
date
userid
在这些情况下(当没有匹配项时),列将是 -- 甚至是联接条件中指定的列。所以这就是我们使用 ,因为我们正在寻找没有找到给定行且更大的行的情况。t2
NULL
WHERE t2.UserId IS NULL
date
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
所以它起作用了,尽管其他地方提到的一些新的东西可能性能更高。
评论
这也将处理重复项(为每个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
这里的答案只有 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);
希望这对(任何人)有所帮助。:)
问候 古斯
评论
刚刚测试过这个,它似乎可以在日志记录表上运行
select ColumnNames, max(DateColumn) from log group by ColumnNames order by 1 desc
只需要在工作中写一个“活生生的”例子:)
此支持在同一日期为 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 提示。
QUALIFY子句难道不是最简单和最好的吗?
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1
作为上下文,在Teradata上,使用此QUALIFY版本在17秒内运行,在23秒内使用“内联视图”/ Aldridge解决方案#1运行。
评论
rank()
rank=1
row_number()
QUALIFY
WHERE
在 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
评论
DISTINCT ON
select VALUE from TABLE1 where TIME =
(select max(TIME) from TABLE1 where DATE=
(select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
select UserId,max(Date) over (partition by UserId) value from users;
评论
没有分区KEEP概念的MySQL解决方案,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
参考: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
评论
我来晚了,但以下 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
这段代码之所以运行得这么好,是因为它只需要扫描一次表格。它不需要任何索引,最重要的是,它不需要对表进行排序,而大多数分析功能都这样做。不过,如果您需要过滤单个用户 ID 的结果,索引将有所帮助。
评论
如果你使用的是 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:
评论
array-agg
array-agg
userid
array_agg
group by
adate
如果您的问题与该页面相似,请检查此链接,那么我建议您进行以下查询,该查询将为该链接提供解决方案
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;
将给出与该链接相关的准确结果
在 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;
使用代码:
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;
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;
使用 ROW_NUMBER()
为每个 分配一个唯一的降序排名,然后过滤到每个 的第一行(即 = 1)。Date
UserId
UserId
ROW_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;
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;
评论
下面的查询可以工作:
SELECT user_id, value, date , row_number() OVER (PARTITION BY user_id ORDER BY date desc) AS rn
FROM table_name
WHERE rn= 1
评论