Vamos supor um cenário com as entidades Person
e Company
uma tabela PersonCompanyStocks
que modela quantas ações uma pessoa possui de uma determinada empresa (cardinalidade N:M). Por exemplo:
person | company | num_stocks
-----------------------------
Alice | foo | 300
Bob | foo | 100
Bob | bar | 200
Esta tabela usa (person, company)
como chave primária para garantir entradas exclusivas e chaves estrangeiras para a respectiva tabela pessoa/empresa (estou usando apenas IDs de string para simplificar).
Agora vamos supor que a empresa bar
compra a empresa foo
. Queremos atualizar a tabela de forma que ela se torne:
person | company | num_stocks
-----------------------------
Alice | bar | 300
Bob | bar | 300
Observar apenas Alice
o registro de 's sugere usar uma abordagem ingênua como:
UPDATE
PersonCompanyStocks
SET
company = "bar"
WHERE
company = "foo"
No entanto, esta atualização falha duplicate key value violates unique constraint ...
porque Bob
já existe uma linha com a chave ("Bob", "bar")
. INSERT
O Postgres de For suporta ON CONFLICT DO ...
, mas parece que não há equivalente para UPDATE
. E claramente também temos que lidar com a fusão adequada do num_stock
valor das duas linhas.
Qual é a melhor estratégia para abordar este problema? Eu só vejo uma solução relativamente feia:
- Uma consulta para determinar as duplicatas.
- Um
UPDATE
para mesclar as duplicatas na linha final. - Um
DELETE
para remover as duplicatas ofensivas. - O acima
UPDATE
para fazer a renomeação em linhas sem duplicatas.
Isso parece complexo e provavelmente é propenso a condições de corrida. O Postgres oferece algum truque para resolver isso de forma mais elegante?
Precisamos mover ações entre empresas, certo? O que significa que precisamos adicionar estoques aos usuários existentes e alterar a empresa para novos usuários. Ou, o mesmo, podemos excluir todas as ações da empresa "foo" e
insert .. on conflict
novas linhas para a empresa "bar"Transacional, sem condições de corrida aqui graças ao bloqueio de linha durante a exclusão.
Seu procedimento é bom. Para evitar condições de corrida, faça tudo em uma única transação e
SELECT ... FOR UPDATE
todas as linhas que você pretende modificar (bloqueio pessimista).