选择一个Id每天的最新记录 - Oracle pl sql

Posted

技术标签:

【中文标题】选择一个Id每天的最新记录 - Oracle pl sql【英文标题】:Select the latest record for an Id per day - 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 中的“最新”相关联?如果是这样,应该如何处理?返回与该人和日期的“最新记录”相关的所有行?只返回其中一个,如果是,是哪一个? (或者“任何一个被捆绑的”都会同样好用吗?) 【参考方案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 对结果进行分组/分区。

以上是关于选择一个Id每天的最新记录 - Oracle pl sql的主要内容,如果未能解决你的问题,请参考以下文章

Oracle PL/SQL:如何根据同一记录的 ID 字段返回字符串值?

Oracle PL/SQL 中自定义生成的 ID/序列? [复制]

Oracle PL/SQL:首先选择一组 id,然后循环/处理它们的方法

oracle 11g 中 for 循环中的 PL/SQL 限制

PL/SQL Oracle 条件等于

PL/SQL Oracle 11g 记录组