логика автоматического приращения в DML - PullRequest
0 голосов
/ 19 сентября 2018

У меня есть две таблицы test2 и test_hist.я хочу загрузить данные в test_hist из test2, но это не удается из-за уникального ограничения.

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 

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Та же основная идея, что и у @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');

Тогда та же аналитическая функция, на одном уровне, еслиВы не против повторить это, включая все столбцы PK в предложении partition-by:

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. 

и выполнение того же оператора в секундуime:

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, если необходимо, в зависимости от ваших фактических данных.

0 голосов
/ 19 сентября 2018

Вы можете попробовать следующее:

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

...