У меня есть 2 таблицы, в которых мне нужно обновить один столбец записями из другой таблицы:
create table abc(id,b,c)as values
('ab1', 1, null)
,('ab2', 2, null)
,('ab3', 3, null)
,('ab4', 4, null) ;
alter table abc
add constraint abc_c_uniq unique(c)
,add constraint abc_pk primary key(id);
create table def(id,e,f,fk) as values
(1, 1, 'text1', 'ab1')
,(2, 2, 'text2', 'ab2')
,(3, 3, 'text3', 'ab3')
,(4, 3, 'text3', 'ab4') ;
alter table def
add constraint def_pk primary key(id)
,add constraint def_fk_fk foreign key (fk) references abc(id);
Мне нужно обновить значения столбца c в таблице abc значениями из f из таблицы def. Столбец c имеет unique constraint, и в таблице f есть повторяющиеся значения для столбца def, но с разными foreign keys, когда я пытаюсь выполнить запрос, я получаю ошибку:
ERROR: duplicate key value violates unique constraint "abc_c_uniq" DETAIL: Key (c)=(text3) already exists.
Вот мой запрос:
UPDATE abc
SET c = def.f
FROM def
WHERE abc.id = def.fk;


Вам нужно решить, что делать в случае, если имеется несколько значений. Вот один из вариантов.
UPDATE abc AS abcTable
SET c = max( defTable.f )
FROM def as defTable
WHERE abcTable.id = abcTable.fk;
Примечание. Напоминаем, что перед запуском этого кода обязательно начните transaction, если это активный db
Итак, меня больше беспокоят дубликаты, о которых Randy тоже упоминалось. То, как я решил эту проблему, будет зависеть исключительно от того, что я делал в прошлом.
При этом, учитывая, что для f в def может быть несколько значений.
Мы можем осторожно использовать его для update, используя его только один раз в update, это будет соответствовать fk в abc.c, а также гарантирует, что мы отфильтруем дубликаты в значениях fk из таблицы def, используя либо первую, либо любую запись, например значение ID или f. в данном случае для каждого fk
Фрагмент кода, который поможет вам:
WITH f_pairs AS (
SELECT DISTINCT ON (defTable.f) defTable.fk, defTable.f
FROM def AS defTable
ORDER BY defTable.f, defTable.id
)
UPDATE abc AS abcTable
SET c = f_pairs.f
FROM f_pairs
WHERE abcTable.id = f_pairs.fk;
Аналогично @Randy's : на самом деле вам следует стремиться сократить количество рассматриваемых fk, а не f. Ваш пример по-прежнему вызывает нарушение: посмотрите это демо
Спасибо обоим за код руководства, я попробовал оба, Min и Max с подзапросом, но с обоими я получил одну и ту же ошибку: duplicate key value violates unique constraint "abc_c_uniq" , поскольку c имеет уникальное ограничение
@GeekDev Я обновил его сейчас, я пропустил болевой момент, который является упущением
@Zegarek Я не знаю, как ты можешь остановить это, спасибо
Спасибо @Zegarek, это обновленное руководство по коду работает очень хорошо, спасибо за помощь.
Вы можете использовать отдельный на , чтобы выбрать только один def.fk для использования: демо в db<>fiddle
update abc
set c = payload.f
from (select distinct on(f)f,fk
from def
order by f,fk) as payload
where abc.id = payload.fk;
select * from abc;
В противном случае, как следует из ошибки, вы в конечном итоге попытаетесь использовать два text3 из def для двух разных строк в abc, нарушая ограничение уникальности.
Идея хорошая, но она не сработает, потому что она скопировала ошибочный
WHERE abcTable.id = abcTable.fk;ОП, хотя, скорее всего, так и должно бытьWHERE abcTable.id = defTable.fk;(вfkнет столбцаabc). Как только это будет исправлено, у вас останетсяERROR: aggregate functions are not allowed in UPDATE. Вы можете исправить это с помощью CTE или подзапроса, но на самом деле вам следует стремиться уменьшить количество рассматриваемыхfk, а неf.