Postgres обработать конфликт ограничений, затем вставить исключенную строку - PullRequest
1 голос
/ 01 августа 2020

Если у меня есть таблица, например:

MyTable
id    date           value    replaced_by_id
1     2020-01-01      10        
2     2020-01-02      20        3
3     2020-01-02      21         

С уникальным ограничением по дате и replace_by, например,

CREATE UNIQUE INDEX my_table_null_test ON my_table (date, (replaced_by_id is null))
WHERE replaced_by_id IS NULL;

Как я могу создать оператор вставки, который устанавливает конфликтующую строку replace_by_id к новому идентификатору строк, а затем вставляет новую строку?

Вдоль строк

insert into my_table (id, date, value) values (gen_id(), '2020-01-02', 21)
on conflict (date, (replaced_by_id is null) ) where replaced_by_id is null 
do update
set replaced_by_id = excluded.id 

**now insert the new row (insert the excluded row)**

, скажем, файл, значения поступают из файла, который имел много значений для та же дата. например,

   date           value
   2020-01-01      10        
   2020-01-02      20     
   2020-01-02      21        
   2020-01-02      22    
   2020-01-02      23    
   2020-01-02      24    
   2020-01-02      22     

приведет к

MyTable
id    date           value    replaced_by_id
1     2020-01-01      10        
2     2020-01-02      20        3
3     2020-01-02      21        4
4     2020-01-02      22        5
5     2020-01-02      23        6
6     2020-01-02      24        7
7     2020-01-02      22           

1 Ответ

0 голосов
/ 01 августа 2020

Я думаю, вы не можете использовать UPSERT для этого требования, потому что UPSERT будет выполнять только операцию обновления в той же таблице или DO Nothing.

Причина:

  1. Вы добавили unique index, что предотвратит вставку с комбинацией существующей даты и нуля поля replace_by_id.

  2. Вы пытаетесь получить идентификатор для обновления существующего записи перед вставкой, что невозможно.

Поэтому для этого нужно использовать обходной путь:

Использование триггеров - Вы должны использовать 2 triggers на вашей таблице один перед вставкой, а другой после вставки, как показано ниже:

Перед триггером вставки

Функция триггера

CREATE FUNCTION trig_beforeinsert()
    RETURNS trigger
    LANGUAGE 'plpgsql'
AS $BODY$
declare 
flg int :=0;
begin
flg=(select count(*) from my_table where date_=new.date_ and replaced_by_id is null);

if flg>0 then
new.replaced_by_id=0;
end if;
return new;
end;

$BODY$;

Триггер в таблице

CREATE TRIGGER my_table_before_insert
    BEFORE INSERT
    ON my_table
    FOR EACH ROW
    EXECUTE PROCEDURE trig_beforeinsert();

После триггера вставки

Функция триггера

CREATE FUNCTION trig_afterinsert()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$
declare 
flg int :=0;
begin

if new.replaced_by_id = 0 then
UPDATE my_table set replaced_by_id=new.id where date_=new.date_ and replaced_by_id is null;
UPDATE my_table set replaced_by_id=null where id=new.id;
end if;
return new;
end;

$BODY$;

Триггер на столе

CREATE TRIGGER my_table_after_insert
    AFTER INSERT
    ON public.my_table
    FOR EACH ROW
    EXECUTE PROCEDURE public.trig_afterinsert();

ДЕМО

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...