Sql 获取列具有最大值的行

Sql 获取列具有最大值的行,sql,oracle,greatest-n-per-group,Sql,Oracle,Greatest N Per Group,表: 我想获得UserId,每个UserId的maxDate值。即具有最新日期的每个用户标识的值。有没有一种方法可以简单地在SQL中实现这一点?最好是神谕 更新:对任何歧义表示歉意:我需要获得所有的用户名。但是对于每个用户ID,只有该用户具有最新日期的那一行。我认为是这样的。请原谅我的语法错误;在这一点上,我已经习惯使用HQL了 编辑:也误读了这个问题!已更正查询 UserId, Value, Date. 我不知道你确切的列名,但应该是这样的: SELECT UserId, Value FRO

表:

我想获得UserId,每个UserId的maxDate值。即具有最新日期的每个用户标识的值。有没有一种方法可以简单地在SQL中实现这一点?最好是神谕


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

我认为是这样的。请原谅我的语法错误;在这一点上,我已经习惯使用HQL了

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

UserId, Value, Date.

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

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

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

T-SQL首先获取所有用户及其maxdate。与表联接以查找maxdates上用户的对应值

select userid, value from users u1 where date = (select max(date) from users u2 where u1.userid = u2.userid) 结果:

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

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

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000
岩石的解析函数

编辑:关于第一条评论

使用分析查询和自连接违背了分析查询的目的

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

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

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


代码有效。

如果UserID、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

我想您应该将此变量用于上一个查询:

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;

假设给定用户ID的日期是唯一的,下面是一些TSQL:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

我知道这是怎么回事。HTH

我认为这应该行得通吗

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

这应该简单到:

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

-


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

SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

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

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

-- 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
我怀疑您可以摆脱外部查询,而将distinct放在内部查询,但我不确定。与此同时,我知道这一个有效

如果你想了解分析查询,我建议你阅读和阅读。以下是简短的总结

在后台,分析查询对整个数据集进行排序,然后按顺序处理。在处理过程中,您根据特定标准对数据集进行分区,然后针对每一行查看一些窗口,默认值为分区中当前行的第一个值-该默认值也是最有效的,可以使用许多分析函数计算值,这些函数的列表与聚合函数非常相似

在本例中,这里是内部查询的作用。整个数据集按UserId排序,然后按Date DESC排序,然后一次处理。对于每一行,返回用户ID和自日期按DESC排序以来该用户ID看到的第一个日期,这是最大日期。这将为您提供重复行的答案。然后,外部不同的压缩重复项


这不是一个特别引人注目的分析查询示例。对于一个更大的胜利,考虑采取一张财政收入表,并计算每个用户和收据,一个运行总额的他们支付。分析查询可以有效地解决这个问题。其他解决方案效率较低。这就是为什么它们是2003 SQL标准的一部分。不幸的是,博士后还没有。Grrr…

我看到很多人使用子查询或窗口函数来实现这一点,但我通常用下面的方式在没有子查询的情况下进行这种查询。它使用简单、标准的SQL,因此它应该可以在任何品牌的RDBMS中工作

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )
@Farhan回复:

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

外部联接尝试将t1与t2联接。默认情况下,返回t1的所有结果,如果t2中有匹配项,也会返回。如果t2中没有与给定的t1行匹配的内容,那么查询仍然返回t1行,并使用NULL作为t2所有列的占位符。一般来说,外部联接就是这样工作的

此查询中的技巧是设计联接的匹配条件,以便t2必须匹配相同的用户ID和更大的日期。这个想法是,如果t2中存在一行具有更大的日期,那么与之比较的t1中的行就不能是该用户ID的最大日期。但是,如果没有匹配项,即如果t2中没有日期大于t1中的行,我们知道t1中的行是给定用户ID中日期最大的行

在这种情况下,当没有匹配时,Column
t2的ns将为NULL-即使是联接条件中指定的列也是如此。这就是为什么我们使用WHERE t2.UserId为NULL,因为我们在搜索没有找到指定UserId日期更大的行的情况。

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

类似这样的情况,可能记不起列列表是否应加括号:

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;
编辑:刚刚真的试过了:

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

因此,它是有效的,尽管其他地方提到的一些新的东西可能更有效。

这也将处理重复的内容,为每个用户id返回一行:

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

这里的答案只有甲骨文。在所有SQL中,这里有一个更复杂的答案:

谁的家庭作业总分最高,谁的家庭作业总分最高

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
还有一个更难的例子,需要一些解释,我没有时间:

给出2008年最受欢迎的图书ISBN和书名,即2008年最常借阅的图书

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)
希望这对任何人都有帮助

问候,,
Guus刚刚测试了这个,它似乎在一个日志表上工作

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

只需要在工作中写一个活生生的例子:

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

栏目: 用户ID、值、日期

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc
您可以使用FIRST_值而不是MAX,并在解释计划中查找它。我没有时间玩它


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

限定子句不是最简单也是最好的吗

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
)

就上下文而言,在Teradata上,此合格版本的测试运行时间为17秒,使用“内联视图”的测试运行时间为23秒。

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

SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

MySQL的解决方案,它没有分区保持、稠密排列的概念

select   UserId,max(Date) over (partition by UserId) value from users;

参考资料:

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

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

这段代码工作得很好的原因是它只需要扫描表一次。它不需要任何索引,最重要的是,它不需要像大多数分析功能那样对表进行排序。不过,如果您需要为单个用户ID筛选结果,索引将有所帮助。

如果您使用Postgres,则可以使用array\u agg like

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

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid
两个查询返回的结果与接受的答案相同。参见SQLFiddles:

在Oracle 12c+中,您可以使用Top n查询和分析函数秩,在不使用子查询的情况下非常简洁地实现这一点:

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 
上面返回每个用户的最大my_日期的所有行

如果您只希望有一行具有最大日期,则用行号替换列组:

用于在降序日期为每个用户标识分配唯一的排名,然后筛选到每个用户标识的第一行,即行\号=1


如果不满足for each UserId条件,会在哪里失败?对于Users中的每个UserID,将保证至少返回一行包含该UserID的内容。或者我遗漏了一个特例?可能不是很有效,史蒂夫。你可能低估了Oracle查询优化器。一点也不。这几乎肯定会被实现为一个带有嵌套循环连接的完整扫描,以获取日期。您所说的逻辑io是表中行数的4倍,对于非平凡的数据量来说是可怕的。仅供参考,不是有效的,但works与works相同,但不是有效的。我们什么时候放弃了将高效作为设计目标?+1,因为当数据表的长度不是数百万行时,这是最容易理解的解决方案。当您有多个不同技能级别的开发人员修改代码时,可理解性在性能上比不引人注意的几分之一秒更重要。我相信您也需要通过用户ID加入。您还需要返回日期值以完全回答问题。如果这意味着另一个first_value子句,那么我建议解决方案比它应该的更复杂,并且基于maxdate的分析方法读起来更好。问题陈述没有提到返回日期。您可以通过添加另一个FIRSTDate或仅通过查询日期并将外部查询更改为GROUP by来实现这一点。我会使用第一个,并期望优化器在一个过程中计算这两个参数。问题语句没有说明返回日期。。。是的,你说得对。很抱歉但增加更多的第一价值条款很快就会变得一团糟。这是一场胜利

dow sort,但是如果您有20列要返回该行,那么您已经编写了大量代码要遍历。我还想到,对于单个用户ID有多行具有最大日期和不同值的数据,此解决方案是不确定的。问题比答案更像是一个错误。我同意这是令人痛苦的冗长。然而,SQL通常不是这样吗?你是对的,解决方案是不确定的。处理关系有多种方法,有时每种方法都是您想要的。如果有多行具有特定用户ID的最大日期值,该怎么办?表的关键字段是什么?下面比较了一些解决方案:@DavidAldridge,该列可能是唯一的。这在PostgreSQL上也很有效。我喜欢它的简单性和通用性——子查询说这是我的条件,外部查询说这是我想看到的细节+哇,比尔。这是我见过的解决这个问题最有创意的方法。在我相当大的数据集上,它的性能也相当好。这肯定比我见过的许多其他解决方案或我自己解决这个难题的尝试都要好。当应用于一个有880万行的表时,这个查询所花费的时间几乎是公认答案的两倍。@Derek:优化取决于RDBMS的品牌和版本,以及MySQL上是否存在适当的索引、数据类型等,这种查询似乎实际上导致它在表之间的笛卡尔连接的结果上循环,从而导致^2时间。相反,使用子查询方法将查询时间从2.0秒减少到0.003s。YMMV.是否有方法调整此选项以匹配日期为最大日期小于或等于用户给定日期的行?例如,如果用户给出了2011年10月23日的日期,并且该表包含2011年10月24日、2011年10月22日、2011年10月20日的行,那么我想要得到2011年10月22日。我挠头读这段代码已经有一段时间了……在我使用一个有大量行的表进行的测试中,这个解决方案花费的时间大约是公认答案的两倍。我确认它比其他解决方案快得多麻烦的是它没有返回完整的结果record@user2067753不,它不会返回完整记录。您可以使用相同的MAX..KEEP。。表达式,因此可以选择所需的所有列。但是,如果您需要大量的列,并且更喜欢使用SELECT*,这就很不方便了。当应用于一个有880万行的表时,这个查询占用了其他一些高投票率答案中查询的一半时间。有人愿意发布一个类似于此的MySQL的链接(如果有的话)吗?这个不能返回重复的吗?例如,如果两行具有相同的用户id和相同的日期,这恰好是最大值。@jastr我认为问题中承认了这一点,而不是最大值。。。结束您也可以在…上使用行号。。。对于排名前n的每个组或排名超过。。。这是我认为最好的答案。但是,在存在联系的情况下,要小心使用秩函数。您可能会得到多个秩=1。如果您确实希望只返回一条记录,最好使用row_number。另外,请注意QUALIFY子句是特定于Teradata的。至少在Oracle中,您必须在包装select语句上使用WHERE子句嵌套查询和筛选,我认为这可能会影响性能。这将返回所有行,而不是每个用户一行。这在其他数据库上也不起作用。这只适用于MySQL,也可能适用于SQL Server,因为它具有类似的变量概念。它肯定不会在Oracle、Postgres、DB2、Derby、H2、HSQLDB、Vertica和Greenplum上工作。另外,公认的答案是标准的ANSI SQL,据我所知,MySQL不支持horse,我想你是对的。我不了解其他数据库或ANSI。我的解决方案能够在MySQL中解决这个问题,MySQL没有适当的支持ANSI SQL以标准方式解决它。与大多数方案相比,这是一个很好的执行计划,但将所有这些技巧应用到多个字段将是乏味的,并且可能会产生不利影响。但是很有趣-谢谢。看你是对的,它可能会变得单调乏味,这就是为什么只有在查询性能需要时才应该这样做。ETL脚本通常就是这样。这非常好。使用Listag做了类似的事情,但看起来很难看。postgres使用array_agg具有更好的替代性。请看我的答案:公认的答案不仅仅是Oracle,它是许多DBMS支持的标准SQL
select   UserId,max(Date) over (partition by UserId) value from users;
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
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
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 
select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;
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 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;