Как устранить ошибку дублирующегося ключа без конфликта с postgresql 11.2 - PullRequest
0 голосов
/ 27 марта 2019

код вставки

INSERT INTO employee (pid, pname, desig, dept, lts_i, lts_O, p_status) VALUES %s \
                    ON CONFLICT (pid) DO UPDATE SET \
                    (pname, desig, dept, lts_i, lts_O, p_status) = \
                    (EXCLUDED.pname, EXCLUDED.desig, EXCLUDED.dept, EXCLUDED.lts_i, EXCLUDED.lts_O, EXCLUDED.p_status) \
                    RETURNING *

Если я вставлю такой как выше, то он работает хорошо. Вместо КОНФЛИКТА я использовал функцию, следующую

CREATE FUNCTION employee_db(
  pid1 integer,
  pname1 text,
  desig1 text,
  dept1 text,
  lts_i1 time,
  lts_o1 time,
  p_status1 text
) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
-- note that "a" must be unique
UPDATE employee SET (lts_i, lts_o, p_status) = (lts_i1, lts_o1, p_status1) WHERE pid = pid1;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO employee(pid, pname, desig, dept, lts_i, lts_o, p_status) VALUES (pid1, pname1, desig1, dept1, lts_i1, lts_o1, p_status1);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- do nothing, and loop to try the UPDATE again
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;

это требует некоторого аргумента

SELECT merge_db(12, 'Newton', 'director', 'd1', '10:00:26', '00:00:00', 'P-Status')"

но когда я обновляю lts_i, lts_O и p_status в пределах одного и того же идентификатора (12)

SELECT merge_db(12, 'Newton', 'director', 'd1', '12:10:22', '02:30:02', 'active')"

тогда также отображается ошибка дубликата ключа. Я не хочу использовать здесь CONFLICT, потому что у меня есть правило UPDATE для той же таблицы, и уже postgresql говорит, что «Событие является одним из SELECT, INSERT, UPDATE или DELETE. Обратите внимание, что INSERT, содержащий предложение ON CONFLICT не может использоваться в таблицах с правилами INSERT или UPDATE. Попробуйте вместо этого использовать обновляемое представление. "

Обновление правила

CREATE RULE log_employee AS ON UPDATE TO employee
    WHERE NEW.lts_i <> OLD.lts_i or NEW.lts_O <> OLD.lts_O
    DO UPDATE employee set today = current_date where id = new.id;

если обновляется lts_i, lts_o или p_status, то будет вставлено current_date в поле "today" в той же таблице сотрудников.

Но определенно мне нужно ПРАВИЛО. В этой ситуации что мне делать?

Любая помощь будет принята с благодарностью. Спасибо.

1 Ответ

1 голос
/ 27 марта 2019

Вы должны использовать триггер для этого.

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

create function emp_trigger_func()
  returns trigger
as
$$
begin
   new.today := current_date;
   return new;
end;
$$
language plpgsql;

Условие, когда эти столбцы должны быть обновлены, лучше выполнять в определении триггера, чтобы избежать ненужного срабатывания триггера

create trigger update_today
  before update on employee
  for each row
  when (NEW.lts_i <> OLD.lts_i or NEW.lts_O <> OLD.lts_O)
  execute procedure emp_trigger_func();

Обратите внимание, что <> неправильно обрабатывает значения NULL. Если lts_i или lts_o могут содержать нулевые значения, тогда условие стрельбы лучше записать как:

  when (   NEW.lts_i is distinct from OLD.lts_i 
        or NEW.lts_O is distinct from OLD.lts_O)

Это также поймает изменение от или до значения null.

...