【问题标题】:how do I query sql for a latest record date for each user如何查询 sql 以获取每个用户的最新记录日期
【发布时间】:2023-01-22 18:18:12
【问题描述】:

我有一个表,它是关于用户何时登录的集合条目。

username, date,      value
--------------------------
brad,     1/2/2010,  1.1
fred,     1/3/2010,  1.0
bob,      8/4/2009,  1.5
brad,     2/2/2010,  1.2
fred,     12/2/2009, 1.3

etc..

如何创建查询以提供每个用户的最新日期?

更新:我忘记了我需要一个与最新日期一致的值。

【问题讨论】:

标签: sql greatest-n-per-group


【解决方案1】:

这是适用于几乎所有数据库引擎的简单老派方法,但您必须注意重复项:

select t.username, t.date, t.value
from MyTable t
inner join (
    select username, max(date) as MaxDate
    from MyTable
    group by username
) tm on t.username = tm.username and t.date = tm.MaxDate

使用窗口函数将避免由于重复的 date 值而导致的重复记录的任何可能问题,因此如果您的数据库引擎允许它,您可以这样做:

select x.username, x.date, x.value 
from (
    select username, date, value,
        row_number() over (partition by username order by date desc) as _rn
    from MyTable 
) x
where x._rn = 1

【讨论】:

  • 使用 postgresql 时,这个版本会比使用 IN(子查询)而不是内部连接更快吗?
  • @TheOne 根据我的经验,使用 inner join 比在 condition 中更快
  • 小心使用这种方法:如果每个用户每个日期有多个记录,它可以为每个用户返回多个行(max(date) 将返回一个连接多个记录的日期)。为避免此问题,最好使用@dotjoe 的解决方案:stackoverflow.com/a/2411763/4406793
  • @RedFilter 这非常适合我的问题。非常感谢这样的技术查询。顺便说一下,我使用 datetime 而不是 date 来避免在特定日期获得多个结果
  • 为什么需要 'and t.date = tm.MaxDate' 分组还不够?
【解决方案2】:

使用窗口函数(适用于 Oracle、Postgres 8.4、SQL Server 2005、DB2、Sybase、Firebird 3.0、MariaDB 10.3)

select * from (
    select
        username,
        date,
        value,
        row_number() over(partition by username order by date desc) as rn
    from
        yourtable
) t
where t.rn = 1

【讨论】:

  • 值得澄清的是哪个 Sybase 产品/版本。它不适用于 Sybase ASE 16。
  • 这种方法的一大好处是它保证每个分区总是只返回一行(username,在这种情况下),甚至不需要唯一的“可订购”字段(比如在其他答案中加入max(date) ).
  • 只是对@MarcoRoy 所说的添加一些内容,如果您碰巧有多个具有相同最大日期的记录,如果您更改查询,例如在调试它时,不同的记录可能会收到行号 1,所以结果可能不一致。不过只要你真的不在乎,那么这应该不是问题。如果您在日期之后添加 PK,则可以解决此问题。例如:order by date desc, id desc)
【解决方案3】:

我看到大多数开发人员都使用内联查询,而没有考虑它对海量数据的影响。

简单地说,您可以通过以下方式实现:

SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username 
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;

【讨论】:

  • 实际上这只适用于重复项,如果你有超过 2 个值,条件 a.date < b.date 不起作用,意思是,它不是一个通用的解决方案,尽管使用 LEFT OUTER JOIN 的想法很重要这个答案中的东西。
  • 有趣的是,Sybase ASE 16 适用于较小的(<10k 行)表,但对于较大的表(>100k 行)它会挂起......我认为这将是关系数据库应该擅长的完美示例......
  • @levantpied ...是的,左连接在较大的数据集上成本很高。如果可能,您可以通过将过滤条件放在连接本身上以某种方式处理它来调整性能。
  • 我不明白的一件事是 IS NULL 部分:如果表自连接基于用户名 (a.username = b.username) 那么我们如何在结果窗口中找到 b.username 为 NULL?
  • @KatherineChen 对于a 中具有最大日期的记录,b 中不会有任何更大的记录。
【解决方案4】:

根据我的经验,最快的方法是获取表中没有更新行的每一行。

另一个优点是使用的语法非常简单,而且查询的含义很容易理解(获取所有行,使得所考虑的用户名不存在更新的行)。

不存在

SELECT username, value
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS witness
  WHERE witness.username = t.username AND witness.date > t.date
);

行_NUMBER

SELECT username, value
FROM (
  SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
  FROM t
) t2
WHERE rn = 1

内部联接

SELECT t.username, t.value
FROM t
INNER JOIN (
  SELECT username, MAX(date) AS date
  FROM t
  GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;

左外连接

SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL

【讨论】:

  • 我很难理解 NOT EXISTS 版本。您是不是在子查询部分缺少聚合?如果我在我的表上运行它,我只能从表中的 40 名员工那里取回 3 条员工记录。我应该至少获得 40 条记录。在内部查询中,我们不应该也通过用户名进行匹配吗?
  • 它适用于我使用以下内容:SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date &gt; t.date AND witness.username = t.username );
  • 我查看了 NOT EXISTS,它看起来只为所有用户返回较高的条目,而不是:“一个查询会给我每个用户的最新日期”。
  • 你确实是对的,我更新了我的查询。谢谢你的评论! @Narshe 抱歉,出于某种原因我错过了您的 cmets:/ 但您是绝对正确的。
  • 这非常好用,非常简单。谢谢!
【解决方案5】:

要获取包含用户最大日期的整行:

select username, date, value
from tablename where (username, date) in (
    select username, max(date) as date
    from tablename
    group by username
)

【讨论】:

  • 为 MySQL 工作
  • 请注意,如果特定用户的同一日期有多个记录,这会给您重复的记录。你可能想要也可能不想要这个。
  • 这个sql在oracle with in clause中比较慢,不会使用索引
【解决方案6】:
SELECT *     
FROM MyTable T1    
WHERE date = (
   SELECT max(date)
   FROM MyTable T2
   WHERE T1.username=T2.username
)

【讨论】:

  • 虽然这是另一种可能的解决方案,但这通常不是解决此问题的好方法。这样做会导致内部查询针对表中的每个名称运行一次,从而导致任何大表的速度大幅下降。执行一个单独的查询,该查询不包含 where 子句中第一个查询的元素,然后将两个表连接起来通常更快。
  • 这确实具有成为非特定于实现的更易于理解的解决方案之一的不错功能。
【解决方案7】:

这应该为您编辑的问题提供正确的结果。

子查询确保只查找最新日期的行,外部 GROUP BY 将处理关系。当同一用户的同一日期有两个条目时,它将返回具有最高value 的条目。

SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
       SELECT username, MAX( date ) date
       FROM your_table
       GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date

【讨论】:

    【解决方案8】:

    如果您的数据库语法支持它,那么 TOP 1 WITH TIES 可以与 ROWNUMER 结合使用。

    使用您提供的示例数据,使用此查询:

    SELECT TOP 1 WITH TIES
      username, date, value
    FROM user_log_in_attempts
    ORDER BY ROW_NUMBER() OVER (PARTITION BY username ORDER BY date DESC)
    

    它产生:

    username | date      | value
    -----------------------------
    bob      | 8/4/2009  | 1.5
    brad     | 2/2/2010  | 1.2
    fred     | 12/2/2009 | 1.3
    

    Demo

    怎么运行的:

    • ROWNUMBER() OVER (PARTITION BY... ORDER BY...) 对于每个用户名,从最年轻的 (rownumber=1) 到最老的 (rownumber=high) 计算行列表
    • ORDER BY ROWNUMBER... 将每个用户最年轻的行排序到顶部,然后是每个用户第二年轻的行,依此类推
    • TOP 1 WITH TIES 因为每个用户都有一个最年轻的行,所以这些最年轻的行在排序标准的意义上是相等的(所有行数都为 1)。所有那些最年轻的行都将被返回。

    使用 SQL-Server 测试。

    【讨论】:

    • 但是 fred 的最新结果是 date=1/3/2010 和 value=1.0
    【解决方案9】:
    SELECT DISTINCT Username, Dates,value 
    FROM TableName
    WHERE  Dates IN (SELECT  MAX(Dates) FROM TableName GROUP BY Username)
    
    
    Username    Dates       value
    bob         2010-02-02  1.2       
    brad        2010-01-02  1.1       
    fred        2010-01-03  1.0       
    

    【讨论】:

    • 如果多个用户在同一天有订单,这可能行不通;如果 brad 和 bob 在 1 月 2 日都有订单怎么办?
    • 我按用户名分组,所以它会工作,结果将是这样的: Username Dates value bob 2010-02-02 1.2 brad 2010-02-02 1.4 fred 2010-01-03 1.0
    【解决方案10】:

    这类似于上面的答案之一,但在我看来它更简单、更整洁。此外,显示了交叉应用语句的良好用途。对于 SQL Server 2005 及更高版本...

    select
        a.username,
        a.date,
        a.value,
    from yourtable a
    cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
    where a.date=b.maxdate
    

    【讨论】:

      【解决方案11】:

      您还可以使用分析等级函数

          with temp as 
      (
      select username, date, RANK() over (partition by username order by date desc) as rnk from t
      )
      select username, rnk from t where rnk = 1
      

      【讨论】:

        【解决方案12】:
        SELECT MAX(DATE) AS dates 
        FROM assignment  
        JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
             paper_submission_detail.PAPER_SUB_ID 
        

        【讨论】:

        • 虽然这段代码可能会解决问题,including an explanation 如何以及为什么解决这个问题确实有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您是在为未来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答以添加解释并指出适用的限制和假设。 From Review
        【解决方案13】:
        SELECT Username, date, value
         from MyTable mt
         inner join (select username, max(date) date
                      from MyTable
                      group by username) sub
          on sub.username = mt.username
           and sub.date = mt.date
        

        将解决更新后的问题。即使有良好的索引,它在大型表上也可能无法很好地工作。

        【讨论】:

          【解决方案14】:
          SELECT *
          FROM ReportStatus c
          inner join ( SELECT 
            MAX(Date) AS MaxDate
            FROM ReportStatus ) m
          on  c.date = m.maxdate
          

          【讨论】:

            【解决方案15】:

            对于Oracle,将结果集降序排列,取第一条记录,所以会得到最新的记录:

            select * from mytable
            where rownum = 1
            order by date desc
            

            【讨论】:

              【解决方案16】:
              SELECT t1.username, t1.date, value
              FROM MyTable as t1
              INNER JOIN (SELECT username, MAX(date)
                          FROM MyTable
                          GROUP BY username) as t2 ON  t2.username = t1.username AND t2.date = t1.date
              

              【讨论】:

              • 关于实施或解释的一两句话对创建高质量答案大有帮助。
              【解决方案17】:

              Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)

              内层查询返回当前用户的最新日期,外层查询根据内层查询结果拉取所有数据。

              【讨论】:

                【解决方案18】:

                我用这种方式为我桌上的每个用户获取最后一条记录。 这是一个根据最近在 PDA 设备上检测到的时间获取销售员最后位置的查询。

                CREATE FUNCTION dbo.UsersLocation()
                RETURNS TABLE
                AS
                RETURN
                Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
                From USERGPS GS
                where year(GS.UTCDateTime) = YEAR(GETDATE()) 
                Group By GS.UserID
                GO
                select  gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
                        from USERGPS gs
                        inner join USER s on gs.SalesManNo = s.SalesmanNo 
                        inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate 
                        order by LastDate desc
                

                【讨论】:

                  【解决方案19】:

                  我的小合集

                  • self join 比 nested select
                  • 但是group by不会给你primary key,这更适合join
                  • 这个密钥可以由partition by结合first_value (docs)给出

                  所以,这是一个查询:

                  选择
                   t.*
                  从
                   表 t 内连接 (
                    选择不同的 first_value(ID) over(partition by GroupColumn order by DateColumn desc) 作为 ID
                    从表
                    其中 FilterColumn = '值'
                   ) t.ID 上的 j = j.ID

                  优点:

                  • 使用where语句使用任意列过滤数据
                  • select过滤行中的任何列

                  缺点:

                  • 需要从 2012 开始的 MS SQL Server。

                  【讨论】:

                    【解决方案20】:

                    我为我的申请做了一些工作:

                    下面是查询:

                    select distinct i.userId,i.statusCheck, l.userName from internetstatus 
                    as i inner join login as l on i.userID=l.userID 
                    where nowtime in((select max(nowtime) from InternetStatus group by userID));    
                    

                    【讨论】:

                      【解决方案21】:
                      SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)
                      

                      【讨论】:

                      • 欢迎来到 StackOverflow,感谢您尝试提供帮助。与解释解决方案的答案相比,像您这样的纯代码答案不太受欢迎。
                      • 请阅读此 how-to-answer 以提供高质量的答案。
                      • 和。它不会为每个用户名返回 MAX,只是返回到最新的单行。
                      【解决方案22】:

                      您将使用聚合函数 MAX 和 GROUP BY

                      SELECT username, MAX(date), value FROM tablename GROUP BY username, value
                      

                      【讨论】:

                      • 您的编辑只会随机选择一个 value,而不是与 MAX(date) 行关联的那个。
                      • 它会给出最大日期,但用户名和值可能不是同一记录。
                      猜你喜欢
                      • 2011-01-25
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-01-10
                      • 2020-07-21
                      • 1970-01-01
                      • 2018-07-09
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多