【问题标题】:Select the latest record for an Id per day - Oracle pl sql选择一个Id每天的最新记录 - Oracle pl sql
【发布时间】:2020-05-29 17:41:01
【问题描述】:

我如何编写一个 sql 语句,它根据 Id 返回每天的最新记录。例如。数据如下。

Id   Name        Comment         Value    DateTime
1    Tim         Test            100      02/06/2020 15:05:12
2    Sue         House           200      03/06/2020 08:25:01
1    Tim         Test            150      02/06/2020 18:05:12
3    Doug        Cars            680      10/05/2019 04:45:10
2    Sue         Tennis          200      03/06/2020 10:35:15

我会得到:

Id   Name        Comment         Value    DateTime
1    Tim         Test            150      02/06/2020 18:05:12
3    Doug        Cars            680      10/05/2019 04:45:10
2    Sue         Tennis          200      03/06/2020 10:35:15

我是否需要按最大日期时间分组的子选择查询?

【问题讨论】:

  • (1) 您的 Oracle 版本是多少?视情况而定,有不同的答案。 (2) 为什么是plsql 标签? (3) 最重要的:能有关系吗?哪里有两行或多行具有相同的 ID 和相同的日期,具有完全相同的时间组件,都与该 goupr 中的“最新”相关联?如果是这样,应该如何处理?返回与该人和日期的“最新记录”相关的所有行?只返回其中一个,如果是,是哪一个? (或者“任何一个被捆绑的”都会同样好用吗?)

标签: sql oracle plsql greatest-n-per-group


【解决方案1】:

我会采用窗口函数:

select id, name, comment, value, dateTime from
(
  select id, name, comment, value, dateTime
           , last_value(dateTime) over( partition by id, trunc(datetime)
                                        order by dateTime 
             rows  BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) lv
) 
where dateTime=lv

【讨论】:

  • 这不分组“每天,每个 ID”
  • @WernfriedDomscheit;你是对的。窗口函数需要额外的 PARTITION BY trunc(DateTime)。我已经在上面编辑了我的答案。
【解决方案2】:

通过使用FIRST/LAST,您甚至不需要子查询:

WITH t(ID, NAME, COMMENT_, VALUE, DateTime) AS (
    SELECT 1,'Tim','Test', 100, TO_DATE('02/06/2020 15:05:12', 'dd/mm/yyyy hh24:mi:ss') FROM dual
    UNION ALL SELECT 2,'Sue','House', 200, TO_DATE('03/06/2020 08:25:01', 'dd/mm/yyyy hh24:mi:ss') FROM dual
    UNION ALL SELECT 1,'Tim','Test', 150, TO_DATE('02/06/2020 18:05:12', 'dd/mm/yyyy hh24:mi:ss') FROM dual
    UNION ALL SELECT 3,'Doug','Cars', 680, TO_DATE('10/05/2019 04:45:10', 'dd/mm/yyyy hh24:mi:ss') FROM dual
    UNION ALL SELECT 2,'Sue','Tennis', 300, TO_DATE('03/06/2020 10:35:15', 'dd/mm/yyyy hh24:mi:ss') FROM dual
    UNION ALL SELECT 2,'Sue','Steets', 400, TO_DATE('10/10/2020 10:35:15', 'dd/mm/yyyy hh24:mi:ss') FROM dual)
SELECT ID, 
    MAX(NAME) KEEP (DENSE_RANK LAST ORDER BY DateTime) AS NAME,
    MAX(COMMENT_) KEEP (DENSE_RANK LAST ORDER BY DateTime) AS COMMENT_,
    MAX(VALUE) KEEP (DENSE_RANK LAST ORDER BY DateTime) AS VALUE,
    MAX(DateTime) KEEP (DENSE_RANK LAST ORDER BY DateTime) AS DateTime
FROM t
GROUP BY ID, TRUNC(DateTime);

+------------------------------------------+
|ID|NAME|COMMENT_|VALUE|DATETIME           |
+------------------------------------------+
|1 |Tim |Test    |150  |02.06.2020 18:05:12|
|2 |Sue |Tennis  |300  |03.06.2020 10:35:15|
|2 |Sue |Steets  |400  |10.10.2020 10:35:15|
|3 |Doug|Cars    |680  |10.05.2019 04:45:10|
+------------------------------------------+

【讨论】:

    【解决方案3】:

    一种简单的方法是使用相关子查询进行过滤:

    select t.*
    from mytable t
    where t.datetime = (
        select max(t1.datetime)
        from mytable t1
        where t1.datetime >= trunc(t.datetime)
        and t1.datetime < trunc(t.datetime) + 1
    )
    

    你可能还喜欢反left join的做法:

    select t.*
    from mytable t
    left join mytable t1 
        on  t1.datetime > t.datetime
        and t1.datetime >= trunc(t.datetime)
        and t1.datetime < trunc(t.datetime) + 1
    where t1.id is null
    

    【讨论】:

    • 如果必须扫描表两次,这些方法的缺点。如果表很大,可能是性能问题
    • @BobC:无论哪种方式,您都需要多次扫描(分析功能在这方面并没有真正的不同)。
    • @GMB - 我不知道你为什么相信,但你肯定错了。 专门以改进为目标开发了分析函数。数据可能会被多次处理(内部查询、外部查询),但存储的数据只能从磁盘读取一次。
    • @gmb,对这两种解决方案运行一个解释计划,您将看到扫描次数。另外,我认为您的解决方案不会产生预期的结果;原始问题要求按 ID 对结果进行分组/分区。
    猜你喜欢
    • 2020-06-21
    • 2012-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 2021-10-04
    • 2021-01-23
    • 1970-01-01
    相关资源
    最近更新 更多