【问题标题】:Add row duplicate on condition in an SQL query在 SQL 查询中按条件添加行重复
【发布时间】:2019-12-25 14:11:42
【问题描述】:

假设有两个数据行:

with
 data_table (title, val_a, val_b) as ( 
    select 'A', 1, 1 from dual union all 
    select 'B', 1, 2 from dual
 ) 
 select *
   from data_table;

我想创建一个 SQL 查询,它在VAL_A <> VAL_B 时复制一行,返回三行,如:

| TITLE | VAL_A | VAL_B |  MARKER    |
--------------------------------------
|  'A'  |  1    |  1    | 'SINGLE'   |
|  'B'  |  1    |  2    | 'MULTIPLE' |
|  'B'  |  1    |  2    | 'MULTIPLE' |

而且从大表data_table(UNION ALL, ...)中选择两次是不合适的。

【问题讨论】:

    标签: sql oracle duplicates


    【解决方案1】:

    您可以使用不等式的自联接:

    with
     data_table (title, val_a, val_b) as ( 
        select 'A', 1, 1 from dual union all 
        select 'B', 1, 2 from dual
     ) 
     select d1.*, nvl2(d2.title,'MULTIPLE','SINGLE') as marker
       from data_table d1
       left join data_table d2 on d1.val_a <> d1.val_b
      where d2.val_a is null
         or ( d2.val_a is not null and d2.val_a = d2.val_b );
    

    Demo

    【讨论】:

    • 由于某种原因感觉比联合查询更干净 +1。
    • 即使没有演示也看起来很棒。
    • 感谢@forpas 的警告,or d1.val_a &gt; d1.val_b 添加了与例外情况相关的内容。
    • @BarbarosÖzhan 问题在于连接和条件。还是不行:dbfiddle.uk/…
    • 我没测试,好像还可以,不过把on d1.val_a &lt; d1.val_b or d1.val_a &gt; d1.val_b改成on d1.val_a &lt;&gt; d1.val_b
    【解决方案2】:

    只是为了好玩,另一种 (ab) 使用递归 CTE (11gR2+) 的方法:

    with
     data_table (title, val_a, val_b) as ( 
        select 'A', 1, 1 from dual union all 
        select 'B', 1, 2 from dual
     ),
     tmp (title, val_a, val_b, marker, flag) as (
        select d.*,
               case when val_a <> val_b then 'MULTIPLE' else 'SINGLE' end,
               case when val_a <> val_b then 1 else 0 end
          from data_table d
        union all
        select title, val_a, val_b, marker, 0
          from tmp
          where flag = 1
     )
     select title, val_a, val_b, marker
       from tmp
      order by title, val_a, val_b;
    
    TITLE      VAL_A      VAL_B MARKER  
    ----- ---------- ---------- --------
    A              1          1 SINGLE  
    B              1          2 MULTIPLE
    B              1          2 MULTIPLE
    

    锚成员使用 case 表达式来决定该行是单行还是多行,并设置一个标志;递归成员用来终止的。

    在这种情况下,它会在一次迭代后终止,这似乎有点矫枉过正。如果需要,尽管它可以适应产生几个额外的行,例如一个代表两个值之间的差异:

    with
     data_table (title, val_a, val_b) as ( 
        select 'A', 1, 1 from dual union all 
        select 'B', 1, 2 from dual union all
        select 'C', 4, 7 from dual
     ),
     tmp (title, val_a, val_b, marker, flag) as (
        select d.*,
               case when val_a <> val_b then 'MULTIPLE' else 'SINGLE' end,
               abs(val_b - val_a)
          from data_table d
        union all
        select title, val_a, val_b, marker, flag - 1
          from tmp
          where flag > 0
     )
     select title, val_a, val_b, marker
       from tmp
      order by title, val_a, val_b;
    TITLE      VAL_A      VAL_B MARKER  
    ----- ---------- ---------- --------
    A              1          1 SINGLE  
    B              1          2 MULTIPLE
    B              1          2 MULTIPLE
    C              4          7 MULTIPLE
    C              4          7 MULTIPLE
    C              4          7 MULTIPLE
    C              4          7 MULTIPLE
    

    就像我说的,只是为了好玩,因为这不是您当前的要求 *8-)

    【讨论】:

      【解决方案3】:

      对于这 2 种情况,使用 UNION ALL:

      with
       data_table (title, val_a, val_b) as ( 
          select 'A', 1, 1 from dual union all 
          select 'B', 1, 2 from dual union all 
          select 'C', 3, 3 from dual union all 
          select 'D', 5, 4 from dual
       ) 
      select * from (
        select t.*, case when t.val_a <> t.val_b then 'MULTIPLE' else 'SINGLE' end marker
        from data_table t
        union all
        select t.*, 'MULTIPLE'
        from data_table t
        where t.val_a <> t.val_b
      )
      order by title
      

      请参阅demo
      结果:

      > TITLE | VAL_A | VAL_B | MARKER  
      > :---- | ----: | ----: | :-------
      > A     |     1 |     1 | SINGLE  
      > B     |     1 |     2 | MULTIPLE
      > B     |     1 |     2 | MULTIPLE
      > C     |     3 |     3 | SINGLE  
      > D     |     5 |     4 | MULTIPLE
      > D     |     5 |     4 | MULTIPLE 
      

      【讨论】:

        【解决方案4】:

        你只想要union all吗?

        with data_table (title, val_a, val_b) as ( 
              select 'A', 1, 1 from dual union all 
              select 'B', 1, 2 from dual
             ) 
        select *
        from data_table
        union all
        select *
        from data_table
        where val_a <> val_b;
        

        【讨论】:

        • 不知道data_table下面隐藏了一个巨大的查询是否适合选择两次。我当然想过union all 解决方案,但我仍在寻找优雅的东西。
        • diziaq 您是否考虑过插入后的触发器? Meaby 它会完成这项工作
        • @Kuba 是的,这不是表格内容,我只需要在任何查询中通过不同的条件来做这个技巧,所以VAL_A &lt;&gt; VAL_B 只是一个简单条件的示例。
        • @diziaq 。 . .这回答了您在此处提出的问题。如果您有其他问题,请将其作为问题提出,而不是在评论中提出。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-13
        • 2018-12-31
        相关资源
        最近更新 更多