【问题标题】:Flag column based on min(date) of different rows each 2 months每 2 个月基于不同行的 min(date) 标记列
【发布时间】:2017-11-27 16:19:52
【问题描述】:

我有一张这样的表格,其中包含不同投诉的信息

telephone   motive  complaint_id    complaint_date
980761524   motive1 R1234561        23/05/2017
980761524   motive1 R1234562        23/05/2017
980761524   motive1 R1234563        25/08/2017
980761524   motive1 R1234564        26/09/2017
980761524   motive1 R1234565        10/10/2017
980761524   motive1 R1234566        30/12/2017
991761525   motive2 R4454222        24/06/2017
991761525   motive2 R4454223        29/06/2017
991761525   motive2 R4454224        30/10/2017
940789563   motive3 R8993271        24/06/2017
940789563   motive3 R8993272        29/06/2017
940789563   motive3 R8993273        30/10/2017

我需要编写一个查询 (oracle-sql),用 repeat_flag(new column) 标记具有相同电话的投诉,相同的动机,min(complaint_date) 为 0 但这个最小值(complaint_date) 每 2 个月更改一次。

让我详细解释一下。

假设我正在分析第一组电话/动机,即

telephone  motive  complaint_id    complaint_date
980761524   motive1 R1234561        23/05/2017
980761524   motive1 R1234562        23/05/2017
980761524   motive1 R1234563        25/08/2017
980761524   motive1 R1234564        26/09/2017
980761524   motive1 R1234565        10/10/2017
980761524   motive1 R1234566        30/12/2017

这个组的最小(投诉日期)是23/05/2017,这里有2行相同的日期。所以只留下一个,我去commitment_id列查看生成的第一个投诉。所有投诉_id 均为R+Number,最低编号为第一次投诉(R1234561)并将其标记为0。因此,如果下一行的日期是第一个 min(complaint_date) 内的 2 个月,我将其标记为 1。

如果下一个投诉日期在两个月内,则您有重复投诉并将其标记为 1。在此示例中,23/05/2017 在范围内,因此我将其标记为 1。

如果下一个投诉日期不在两个月内,则不是重复,您将其标记为“0”。现在,这里的投诉日期是 25/08/2017,现在是新的 min(complaint_date)

下一行,这里我得到了26/09/2017,这是在 2 个月内25/08/2017,所以这是重复的,我将其标记为 1。

下一行,这里我得到了10/10/2017,这是在 2 个月内25/08/2017,所以这是重复的,我将其标记为 1。

最后,我得到了30/12/2017,它不在两个月内25/08/2017,所以这不再重复,我将其标记为。现在这是新的 min(complaint_date)

其他行也一样。

最终的结果应该是这样的

telephone   motive  complaint   complaint_date  2months_repeat_flag
980761524   motive1 R1234561        23/05/2017                   0
980761524   motive1 R1234562        23/05/2017                   1
980761524   motive1 R1234563        25/08/2017                   0
980761524   motive1 R1234563        26/09/2017                   1
980761524   motive1 R1234563        10/10/2017                   1
980761524   motive1 R1234563        30/12/2017                   0

决赛桌应该是这个样子

telephone   motive  complaint   complaint_date  2months_repeat_flag
980761524   motive1 R1234561        23/05/2017                    0
980761524   motive1 R1234562        23/05/2017                    1
980761524   motive1 R1234563        25/08/2017                    0
980761524   motive1 R1234564        26/09/2017                    1
980761524   motive1 R1234565        10/10/2017                    1
980761524   motive1 R1234566        30/12/2017                    0
991761525   motive2 R4454222        24/06/2017                    0
991761525   motive2 R4454223        29/06/2017                    1
991761525   motive2 R4454224        30/10/2017                    0
940789563   motive3 R8993271        24/06/2017                    0
940789563   motive3 R8993272        29/06/2017                    1
940789563   motive3 R8993273        30/10/2017                    0

我的桌子上有大约 2 到 30 起投诉,使用相同的电话和相同的动机。

是否可以仅使用 SQL?欢迎任何存储过程。但我更喜欢只使用 SQL 来执行此操作,因为我的用户无权创建 sp。

【问题讨论】:

  • 您好!欢迎来到堆栈溢出!您能否提供一些示例数据?很高兴在这里看到对您的问题的解释以及您需要什么,但还提供示例插入语句非常有帮助,以便其他人可以轻松地重新创建一些虚拟数据以使用。

标签: sql oracle date oracle11g


【解决方案1】:

对于 Oracle 12.1 及更高版本,这是一个使用 match_recognize 子句的解决方案。 OP 应该尝试各种解决方案(无论如何解决问题的解决方案),看看哪种解决方案对他/她的特定数据最有效。

select telephone, motive, complaint_id, complaint_date,
       case classif when 'A' then 0 else 1 end
from   test_data
match_recognize (
  partition by telephone, motive
  order by complaint_date, complaint_id
  measures classifier() as classif
  all rows per match
  pattern ( a x* )
  define  x as complaint_date <= add_months(a.complaint_date, 2)
)
order by telephone, motive, complaint_date, complaint_id
;

【讨论】:

    【解决方案2】:

    另一种解决方案是基于递归CONNECT BY 子句

    SELECT t.*,
           CASE WHEN t1.complaint_id IS NULL
           THEN 1 ELSE 0 
           END As two_months_repeat_flag
    FROM test_data t
    LEFT JOIN (
        SELECT complaint_id
        FROM (
            select t.complaint_id,
                   (
                     SELECT min( complaint_id ) KEEP (DENSE_RANK FIRST ORDER BY complaint_date, complaint_id )
                     FROM test_data t1
                     WHERE t1.telephone = t.telephone
                       AND t1.motive = t.motive 
                       AND t1.complaint_date > add_months( t.complaint_date, 2 )
                   ) as next_complaint_id,
                   row_number() over (partition by telephone, motive order BY complaint_date, complaint_id) as rn
            from test_data t
        )
        start with rn = 1
        connect by complaint_id = prior next_complaint_id
    ) t1
    ON t.complaint_id = t1.complaint_id
    ORDER BY 1, 2,4,3
    

     TELEPHONE MOTIVE  COMPLAIN COMPLAINT_ TWO_MONTHS_REPEAT_FLAG
    ---------- ------- -------- ---------- ----------------------
     940789563 motive3 R8993271 24/06/2017                      0
     940789563 motive3 R8993272 29/06/2017                      1
     940789563 motive3 R8993273 30/10/2017                      0
     980761524 motive1 R1234561 23/05/2017                      0
     980761524 motive1 R1234562 23/05/2017                      1
     980761524 motive1 R1234563 25/08/2017                      0
     980761524 motive1 R1234564 26/09/2017                      1
     980761524 motive1 R1234565 10/10/2017                      1
     980761524 motive1 R1234566 30/12/2017                      0
     991761525 motive2 R4454222 24/06/2017                      0
     991761525 motive2 R4454223 29/06/2017                      1
     991761525 motive2 R4454224 30/10/2017                      0
    
    12 rows selected. 
    

    【讨论】:

    • 这会起作用,但它可能会很慢(由于代码最里面的部分需要相关子查询)。在 11.2 及更高版本中,可以编写递归 CTE 以避免大部分不需要的工作,但它可能仍然比 model 方法慢。无论如何,为正确的方法和解决方案 +1。
    【解决方案3】:

    或者您可以使用 Oracle 的 LAG 函数,让您访问上一行的值:

     SELECT telephone,
            motive,
            complaint_id,
            complaint_date,
            CASE WHEN 
               ADD_MONTHS(LAG  (complaint_date, 1 , NULL) 
                            OVER (PARTITION BY  telephone, motive 
                                  ORDER BY complaint_date),2) > complaint_date   
                 THEN 1 ELSE 0  END AS flag
       FROM test_data
    

    此解决方案假定您想要自上次投诉以来的差异 - 而不是第一次投诉。

    【讨论】:

    • OP给出的详细示例表明该假设是错误的。你读过OP的问题吗?
    • 您好,您能否解决此 LAG 函数以匹配与第一次投诉的差异?这会很有用。谢谢
    • @gabuglc - 不幸的是,这是不可能的。仅靠分析函数无法解决您提出的问题。
    【解决方案4】:

    这是一个适用于 Oracle 10 及更高版本的解决方案。它使用model 子句(match_recognize 子句的远祖)。 match_recognize,在 Oracle 12 及更高版本中可用,可能快几倍,但可能不适用于 OP(问题标记为 oracle11g)。

    设置

    alter session set nls_date_format = 'dd/mm/yyyy';
    
    create table test_data ( telephone, motive, complaint_id, complaint_date ) as
        select 980761524, 'motive1', 'R1234561', to_date('23/05/2017') from dual union all
        select 980761524, 'motive1', 'R1234562', to_date('23/05/2017') from dual union all
        select 980761524, 'motive1', 'R1234563', to_date('25/08/2017') from dual union all
        select 980761524, 'motive1', 'R1234564', to_date('26/09/2017') from dual union all
        select 980761524, 'motive1', 'R1234565', to_date('10/10/2017') from dual union all
        select 980761524, 'motive1', 'R1234566', to_date('30/12/2017') from dual union all
        select 991761525, 'motive2', 'R4454222', to_date('24/06/2017') from dual union all
        select 991761525, 'motive2', 'R4454223', to_date('29/06/2017') from dual union all
        select 991761525, 'motive2', 'R4454224', to_date('30/10/2017') from dual union all
        select 940789563, 'motive3', 'R8993271', to_date('24/06/2017') from dual union all
        select 940789563, 'motive3', 'R8993272', to_date('29/06/2017') from dual union all
        select 940789563, 'motive3', 'R8993273', to_date('30/10/2017') from dual
    ;
    
    commit;
    

    查询

    select telephone, motive, complaint_id, complaint_date, flag
    from   test_data
    model  
      partition by (telephone, motive)  
      dimension by (row_number() over (partition by telephone, motive 
                                       order by     complaint_date, complaint_id) rn)
      measures (complaint_id, complaint_date, complaint_date s, 0 flag)  
      rules (  
        s[rn>1]    = case when complaint_date[cv(rn)] < add_months(s[cv(rn) - 1], 2)  
                          then s[cv(rn) - 1]  
                          else complaint_date[cv(rn)]  
                     end,
        flag[rn>1] = case when s[cv(rn)] = s[cv(rn) - 1] then 1 else 0 end
      )
    order by telephone, motive, rn
    ;
    

    输出:(通过电话订购,然后按动机订购 - 可根据需要更改)

     TELEPHONE   MOTIVE    COMPLAINT_ID COMPLAINT_DATE FLAG
     ---------   -------   ------------ -------------- ----
     940789563   motive3   R8993271     24/06/2017        0
     940789563   motive3   R8993272     29/06/2017        1
     940789563   motive3   R8993273     30/10/2017        0
     980761524   motive1   R1234561     23/05/2017        0
     980761524   motive1   R1234562     23/05/2017        1
     980761524   motive1   R1234563     25/08/2017        0
     980761524   motive1   R1234564     26/09/2017        1
     980761524   motive1   R1234565     10/10/2017        1
     980761524   motive1   R1234566     30/12/2017        0
     991761525   motive2   R4454222     24/06/2017        0
     991761525   motive2   R4454223     29/06/2017        1
     991761525   motive2   R4454224     30/10/2017        0
    

    【讨论】:

    • 太棒了!这非常有效。你介意分享 oracle12 吗?
    • @gabuglc - 我刚刚在一个单独的答案中发布了 Oracle 12 解决方案(因为它确实是一个不同的答案)。您应该测试不同的解决方案,看看哪个最有效。经验表明,如果 Oracle 12 可用,match_recognize 将具有显着优势,但最好的确定方法是测试。
    猜你喜欢
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 2016-12-10
    • 1970-01-01
    相关资源
    最近更新 更多