繁体   English   中英

Oracle - 确定以前的记录是否存在

[英]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

在没有前一行或前一行具有不同categorytype值的每个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来处理categorytype_可能为null的可能性(您没有具体说任何一种方式,所以我想从开始)。 我假设从null值更改为非null值,反之亦然,被视为“更新”。

请注意, typedate都是 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM