【问题标题】:auto increment logic in DMLDML 中的自动递增逻辑
【发布时间】:2018-09-19 16:14:04
【问题描述】:

我有两个表 test2 和 test_hist。我想将数据从 test2 加载到 test_hist 但由于唯一约束而失败。

CREATE TABLE TEST2 (ID NUMBER , TEXT VARCHAR2(10));
create table test_hist (id number , text varchar2(10) , constraint t_pk PRIMARY key (id , text));

INSERT INTO TEST2 VALUES(100 , '20180909-I');
INSERT INTO TEST2 VALUES(101 , '20180909-I');
INSERT INTO TEST2 VALUES(102 , '20180809-I');
INSERT INTO TEST2 VALUES(100 , '20180909-I');
COMMIT;

INSERT INTO test_hist SELECT ID , TEXT FROM TEST2;

每当有如下重复项时,我想在 TEXT 字段中附加自动递增编号。

expected OUTPUT 

ID         TEXT
100      20180909-I
101      20180909-I
102      20180809-I
100      20180909-I-1
100      20180909-I-2
102      20180809-I-1

任何人都可以帮助我实现这一目标。 提前致谢

如果我多次执行插入语句,它应该使用自动增量文本插入到 test_hist 中。 例如

insert into test_hist
select id,
  text || case when row_number() over (partition by id, text order by null) > 1
          then (1 - row_number() over (partition by id, text order by null)) end
from test2;

9 rows inserted.

select *
from test_hist
order by id, text;

        ID TEXT        
---------- ------------
       100 20180909-I  
       100 20180909-I-1
       100 20180909-I-2
       100 20180909-I-3
       101 20180909-I  
       101 20180909-I-1
       102 20180809-I  
       102 20180809-I-1
       102 20180809-I-2 

【问题讨论】:

    标签: sql oracle sql-insert analytic-functions


    【解决方案1】:

    与@Barbaros 的基本思想相同,但排列方式略有不同,并且第二个表的列大小增加了,因此它可以容纳修改后的值:

    create table test2 (
      id number, text varchar2(10)
    );
    
    create table test_hist (id number,
      text varchar2(12), -- increased size to 12; may need to be larger
      constraint t_pk primary key (id , text)
    );
    
    insert into test2 values(100 , '20180909-I');
    insert into test2 values(101 , '20180909-I');
    insert into test2 values(102 , '20180809-I');
    insert into test2 values(100 , '20180909-I');
    insert into test2 values(100 , '20180909-I');
    insert into test2 values(102 , '20180809-I');
    

    然后是同一个解析函数,如果你不介意重复的话,在一个级别中,并在 partition-by 子句中包括所有 PK 列:

    insert into test_hist
    select id,
      text || case when row_number() over (partition by id, text order by null) > 1
              then (1 - row_number() over (partition by id, text order by null)) end
    from test2;
    
    6 rows inserted.
    
    select *
    from test_hist
    order by id, text;
    
            ID TEXT        
    ---------- ------------
           100 20180909-I  
           100 20180909-I-1
           100 20180909-I-2
           101 20180909-I  
           102 20180809-I  
           102 20180809-I-1
    

    如果您在实际场景中实际上有更多列,并且原始表中有另一个非 PK 列您想影响历史记录行的递增顺序,您可以在函数的 order by 中使用它null,实际上只是一个虚拟的占位符。


    如果它需要在多次插入时继续做同样的事情(这表明数据模型问题甚至比原始要求更严重),那么您还需要计算历史表中现有的匹配项。

    从与之前相同的原始数据和一个空的历史表开始:

    insert into test_hist
    select id,
      text || case when appearance > 1 then (1 - appearance) end
    from (
      select t.id,
        t.text,
        row_number() over (partition by t.id, t.text order by null) + (
          select count(*) from test_hist th
          where th.id = t.id
          and th.text like t.text || '%'
        ) as appearance
      from test2 t
    );
    
    6 rows inserted.
    
    select *
    from test_hist
    order by id, text;
    
            ID TEXT        
    ---------- ------------
           100 20180909-I  
           100 20180909-I-1
           100 20180909-I-2
           101 20180909-I  
           102 20180809-I  
           102 20180809-I-1
    
    6 rows selected. 
    

    并再次运行相同的语句:

    insert into test_hist
    select id,
      text || case when appearance > 1 then (1 - appearance) end
    from (
      select t.id,
        t.text,
        row_number() over (partition by t.id, t.text order by null) + (
          select count(*) from test_hist th
          where th.id = t.id
          and th.text like t.text || '%'
        ) as appearance
      from test2 t
    );
    
    6 rows inserted.
    
    select *
    from test_hist
    order by id, text;
    
            ID TEXT        
    ---------- ------------
           100 20180909-I  
           100 20180909-I-1
           100 20180909-I-2
           100 20180909-I-3
           100 20180909-I-4
           100 20180909-I-5
           101 20180909-I  
           101 20180909-I-1
           102 20180809-I  
           102 20180809-I-1
           102 20180809-I-2
           102 20180809-I-3
    
    12 rows selected. 
    

    可能有一些方法可以对其进行优化,这样您就不必经常访问历史记录表,但这可能会给您一个工作的起点。

    like 的使用实际上意味着这仅在文本始终具有相同长度的情况下才有效,或者至少您不能拥有其他值的扩展值;否则你会得到比你想要的更多的匹配。至少从您的示例数据来看,这看起来不是问题。如有必要,您可以通过切换到regexp_like 来解决这个问题,具体取决于您的实际数据。

    【讨论】:

    • 谢谢..它部分解决了目的。实际上,如果我多次执行相同的插入语句,它应该在历史表中插入相同的记录,并带有自动增量文本,如_1,_2 ...等。但在这种情况下,我在第二次执行后遇到唯一约束错误。基表中的数据保持不变,即 test2。
    • 嗨,我有什么办法可以做到这一点?
    • @kashi - 我添加了快速尝试解决方案,但可能有更好/更有效的解决方案。
    【解决方案2】:

    您可以尝试以下方法:

    select ID, text||decode(rn-1,0,null,'-'||(rn-1)) as text
    from
    (
    with test2(rnk,ID,text) as
    (
        select 1, 100 , '20180909-I' from dual union all
        select 2, 101 , '20180909-I' from dual union all
        select 3, 102 , '20180809-I' from dual union all
        select 4, 100 , '20180909-I' from dual union all
        select 5, 100 , '20180909-I' from dual union all
        select 6, 102 , '20180909-I' from dual
        )
        select t.ID, t.rnk, 
               t.text, row_number() over (partition by ID order by Text,ID) as rn
          from test2 t
    )
     order by rn, rnk
    

    Rextester Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      • 2012-02-27
      • 2016-01-12
      • 1970-01-01
      • 2021-05-08
      • 2016-03-07
      相关资源
      最近更新 更多