【问题标题】:Oracle - Identify If Previous Record ExistsOracle - 确定以前的记录是否存在
【发布时间】:2019-08-31 02:04:37
【问题描述】:

我有这样的输入日期:

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 年 2 月 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

【问题讨论】:

  • PL/SQL 是一种命令式编程语言,由 Oracle 创建,用于与其 SQL 版本协同工作。您的问题与 PL/SQL 完全无关 - 它与 Oracle SQL 有关。我更改了您的标题和标签以反映这一点。不要感觉太糟糕 - 很多人(错误地)认为 PL/SQL 只是意味着“Oracle SQL”。
  • 如果 123 的第一行的类型是小而不是大,您希望输出什么?
  • 你如何定义“最近的日期”?您是说每个 id_number 的“最近日期”吗?而且,为什么id = 123 不包含在您的输出中?是不是因为自上一行以来没有任何变化(意思是:categorytype 和以前一样)?
  • 感谢@mathguy!我会将最近的日期定义为 2/1/2019 - 这将是报告日期。任何日期都可以是报告日期,因此如果我想查看截至 2019 年 1 月 20 日哪些记录是新的或更新的,我想查看该记录及其之前的两条记录。正确 - 自给定 id_number 的上一行以来,123 没有更改
  • @Boneist 输出将保持不变,因为 123 与当前报告日期(2019 年 2 月 1 日)相比没有变化,并且上次 123 出现在表格中。

标签: sql oracle


【解决方案1】:

您可以使用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

【讨论】:

    【解决方案2】:

    在没有前一行或前一行具有不同值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 );
    

    Fiddle

    【讨论】:

    • 优雅、简洁,并使用 OP 尝试使用的方法(分区依据)。
    【解决方案3】:

    您可以执行以下操作:

    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)
    

    【讨论】:

    • last_val.rn = 2 这是在做什么?那么这种方法是否需要在每个字段之间指定不等式?
    • @OverflowingTheGlass 这不像预期的那样工作,您的DATE 列不是唯一的。这是对 id_number “组”的相等日期值的非确定性(随机)排序..
    • @RaymondNijland 谢谢 - 你是对的,日期列不是唯一的
    • @OverflowingTheGlass 不要介意我的最后一条评论,假设 id 列是 99.9999999 % 的所有案例都有一个主键或唯一键,在这种情况下不是这种情况......但你可以使用 Oracle 的 @987654324 @ 现在我重新考虑它 row_number() over (partition by id_number order by DATE desc, ROWID DESC) 使其始终具有确定性(固定)排序..
    • 对于它是如何工作的/它如何用于特定日期(即日期为 2/1/2019 的记录是更新的还是新的)仍然有点困惑
    【解决方案4】:

    这是我想出的。我试图编写一个高效的查询(而不是一个易于阅读的);有很多东西要解压,所以与其解释一切,我会等你来澄清你不明白的任何事情。

    在查询中,我使用 WITH 子句来模拟输入数据,并使用 CTE 来创建报告日期。我对报告日期进行了硬编码,但可以(并且应该)将其更改为绑定变量,以便它可以成为用户输入。

    然后,从输入数据中,我只读取日期为 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 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-09
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 2012-03-06
      • 2015-03-24
      • 2011-09-25
      相关资源
      最近更新 更多