[英]Oracle - Identify If Previous Record Exists
我有这样的输入日期:
id_number category type date
123 horse big 1/1/2019
123 horse big 1/15/2019
234 pig small 1/20/2019
123 horse big 2/1/2019
234 pig medium 2/1/2019
345 dog tiny 2/1/2019
对于最近日期 (2/1/2019) 的记录,我想检索哪些记录(由id_number
标识)是全新的且已更新。
想要的结果:
id_number category type date new_or_updated
234 pig medium 2/1/2019 updated
345 dog tiny 2/1/2019 new
记录234
已更新,因为type
已从先前记录更改。 记录345
是全新的,因为之前没有记录是id_number
345
。 2019 年 1 月 1 日的记录123
被排除在外,因为它不是新记录,并且不是从 2019 年 1 月 15 日发生的先前123
记录更新的。
如何识别新记录和更新记录?
我曾尝试使用row_number()
按除开始date
以外的所有字段进行分区,但我不确定从那里开始,并且row_number()
未按预期工作,因为它为记录分配了不同的数字而没有差异date
之外。
select row_number() over (partition by id_number, category, type order by id_number),
id_number,
category,
type,
date
from schema.table
在没有前一行或前一行具有不同category
或type
值的每个ID_NUMBER
查找最近的行
select id_number, category, type, dat
, case when pId is null then 'new' else 'updated' end new_or_updated
from (
select id_number, category, type, dat
, max(dat) over(partition by ID_NUMBER) dmax
, lag(ID_NUMBER) over(partition by ID_NUMBER order by dat) pId
, lag(category) over(partition by ID_NUMBER order by dat) pCategory
, lag(type) over(partition by ID_NUMBER order by dat) pType
from tbl
) t
where dat = dmax and (pId is null or pCategory != Category or pType !=Type );
您可以使用lag()
和一些相当简单的逻辑来做到这一点。 简化的逻辑是查看上一个日期,一次查看 id,一次查看 id 和其他列。
我认为“更新”意味着从上次出现开始更新(而不是曾经更新过)。 如果是这样的话:
select t.*,
(case when prev_id_date is null then 'New'
when prev_id_date <> prev_ict_date or prev_ict_date is null
then 'Updated'
else 'Same'
end)
from (select t.*,
lag(date) over (partition by id_number) as prev_id_date,
lag(date) over (partition by id_number, category, type) as prev_ict_date,
row_number() over (partition by id_number order by date desc) as seqnum
from t
) t
where seqnum = 1;
这包括“相同”的记录。 当然,您可以通过将where
子句调整为:
where seqnum = 1 and prev_ict_date = prev_date
您可以执行以下操作:
WITH dat AS (SELECT row_number() over (partition by id_number order by DATE desc) rn
, id_number
, category
, TYPE
, date
from schema.TABLE)
SELECT dat.*
, CASE WHEN last_val.id_number IS NULL THEN 'new'
WHEN last_val.category != dat.category
OR last_val.TYPE != dat.TYPE
THEN 'updated'
END new_or_updated
FROM dat
LEFT JOIN dat LAST_val
ON last_val.id_number = dat.id_number
AND last_val.rn = 2
WHERE rn=1
AND (last_val.id_number IS NULL
OR last_val.category != dat.category
OR last_val.TYPE != dat.TYPE)
这是我想出的。 我试图编写一个高效的查询(而不是一个易于阅读的查询); 有很多东西要解开,所以我不会解释所有的东西,我会等你对任何你不理解的东西进行澄清。
在查询中,我使用 WITH 子句来模拟输入数据,并使用 CTE 来创建报告日期。 我对报告日期进行了硬编码,但是可以(并且应该)将其更改为绑定变量,以便它可以成为用户输入。
然后,从输入数据中,我只读取日期 <= 报告日期的行,然后按 ID 聚合。 查询的核心是having
子句,我反复使用第first/last
聚合函数; 如果您不熟悉它,请先阅读它。 (遗憾的是,很多开发人员都没有。)此外,在having
子句中,我使用decode
来处理category
或type_
可能为null
的可能性(您没有具体说任何一种方式,所以我想从开始)。 我假设从null
值更改为非null
值,反之亦然,被视为“更新”。
请注意, type和date都是 Oracle 关键字,它们不应用作列名。 我在查询中添加了尾随下划线。
with test_data (id_number, category, type_, date_) as (
select 123, 'horse', 'big' , to_date('1/1/2019' , 'mm/dd/yyyy') from dual union all
select 123, 'horse', 'big' , to_date('1/15/2019', 'mm/dd/yyyy') from dual union all
select 234, 'pig' , 'small' , to_date('1/20/2019', 'mm/dd/yyyy') from dual union all
select 123, 'horse', 'big' , to_date('2/1/2019' , 'mm/dd/yyyy') from dual union all
select 234, 'pig' , 'medium', to_date('2/1/2019' , 'mm/dd/yyyy') from dual union all
select 345, 'dog' , 'tiny' , to_date('2/1/2019' , 'mm/dd/yyyy') from dual
)
, report_inputs (dt) as (
select to_date('2/1/2019', 'mm/dd/yyyy') from dual
)
select t.id_number as id_number,
max(t.category) keep (dense_rank last order by t.date_) as category,
max(t.type_) keep (dense_rank last order by t.date_) as type_,
r.dt as date_,
case when count(*) = 1 then 'new' else 'updated' end as new_or_updated
from test_data t join report_inputs r on t.date_ <= r.dt
group by t.id_number, r.dt
having max(t.date_) = r.dt
and
(
count(*) = 1
or
decode(max(t.category) keep (dense_rank last order by nullif(t.date_, r.dt)
nulls first),
max(t.category) keep (dense_rank last order by t.date_), 1) is null
or
decode(max(t.type_) keep (dense_rank last order by nullif(t.date_, r.dt)
nulls first),
max(t.type_) keep (dense_rank last order by t.date_), 1) is null
)
;
输出:
ID_NUMBER CATEGORY TYPE_ DATE_ NEW_OR_UPDATED
--------- -------- ------ ---------- --------------
234 pig medium 02/01/2019 updated
345 dog tiny 02/01/2019 new
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.