«Немедленным» ответом было бы просто запустить оператор DELETE, а затем выполнить оператор INSERT в одной транзакции.
Если вы хотите, например, избегать повторяющихся имен пользователей, тогда вы можете сделать что-то вроде этого:
begin transaction
delete from account
where username = 'arthur';
insert into account(username, password, email, created_on)
values ('arthur', '****', 'arthur@h2g2.com', current_timestamp);
commit;
Вы можете объединить это в одно утверждение, но это не такЭто не имеет большого значения:
with new_values (username, password, email, created_on) as (
values values ('arthur', '****', 'arthur@h2g2.com', current_timestamp);
), deleted as (
delete from the_table
where username = (select username from new_values)
)
insert into account
select *
from new_values;
Единственное преимущество в том, что вам не нужно повторять значения дважды.
Однако, если на account
ссылаются другие таблицы (т. Е. Внешний ключ «указывает» на the_table
), это не будет работать, так как DELETE не будет работать, если на строку все еще ссылаются.
Лучшее решение состоит в том, чтобы использовать INSERT ON CONFLICT
и затем обновить существующую строку новыми данными:
insert into account(username, password, email, created_on)
values ('arthur', '****', 'arthur@h2g2.com', current_timestamp)
on conflict (username)
do update
set password = excluded.password,
email = excluded.email;
Однако это все равно выдаст ошибку, если электронное письмо уже существует, но, к сожалению,для on conflict do update
можно указать только одно уникальное ограничение.
Чтобы справиться с двумя различными уникальными ограничениями, все становится немного сложнее:
with new_values (username, password, email, created_on) as (
values
('arthur', '***', 'arthur@h2g2.com', current_timestamp)
), inserted as (
insert into account(username, password, email, created_on)
select * from new_values
on conflict do nothing
returning id
)
update account
set password = nv.password
from new_values nv
where (account.username = nv.username or account.email = nv.email)
and not exists (select * from inserted);
Сначала делается попытка вставки.Если какое-либо уникальное ограничение нарушается, вставка просто игнорируется (on conflict do nothing
).
Окончательный оператор UPDATE выполняется только в том случае, если на предыдущем шаге не было вставлено ни одной строки.Это достигается с помощью and not exists (select * from inserted
.
Поскольку в качестве имени пользователя или электронное письмо могло вызвать нарушение ограничения, обновление использует условие или в этих двух столбцах для обновления существующей строки.Если вы хотите, вы также можете обновить больше столбцов там.