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