Estilo de perguntas e respostas
Bem, depois de pesquisar e lutar com o problema por horas, descobri que existem duas maneiras de fazer isso, dependendo da estrutura da sua tabela e se você tem restrições de chaves estrangeiras ativadas para manter a integridade. Eu gostaria de compartilhar isso em um formato limpo para economizar algum tempo para as pessoas que podem estar na minha situação.
Opção 1: você pode excluir a linha
Em outras palavras, você não tem uma chave estrangeira ou, se as tiver, seu mecanismo SQLite está configurado para que não haja exceções de integridade. O caminho a seguir é INSERT OR REPLACE . Se você está tentando inserir / atualizar um jogador cujo ID já existe, o mecanismo SQLite irá deletar aquela linha e inserir os dados que você está fornecendo. Agora surge a pergunta: o que fazer para manter o antigo ID associado?
Digamos que queremos UPSERT com os dados user_name = 'steven' e idade = 32.
Olhe para este código:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
O truque está em coalescer. Ele retorna o id do usuário 'steven' se houver, e caso contrário, ele retorna um novo id novo.
Opção 2: você não pode excluir a linha
Depois de mexer na solução anterior, percebi que no meu caso isso poderia acabar destruindo dados, já que esse ID funciona como uma chave estrangeira para outra tabela. Além disso, criei a tabela com a cláusula ON DELETE CASCADE , o que significaria que ela apagaria os dados silenciosamente. Perigoso.
Então, primeiro pensei em uma cláusula IF, mas o SQLite só tem CASE . E este CASE não pode ser usado (ou pelo menos eu não consegui) para realizar uma consulta UPDATE se EXISTS (selecione id de jogadores onde user_name = 'steven'), e INSERT se não. Não vá.
E então, finalmente, usei a força bruta, com sucesso. A lógica é, para cada UPSERT que você deseja realizar, primeiro execute um INSERT OU IGNORE para ter certeza de que há uma linha com nosso usuário e, em seguida, execute uma consulta UPDATE com exatamente os mesmos dados que você tentou inserir.
Os mesmos dados de antes: user_name = 'steven' e idade = 32.
-- make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);
-- make sure it has the right data
UPDATE players SET user_name='steven', age=32 WHERE user_name='steven';
E isso é tudo!
EDITAR
Como Andy comentou, tentar inserir primeiro e depois atualizar pode levar ao disparo de gatilhos com mais frequência do que o esperado. Isso não é, em minha opinião, um problema de segurança de dados, mas é verdade que disparar eventos desnecessários faz pouco sentido. Portanto, uma solução melhorada seria:
-- Try to update any existing row
UPDATE players SET age=32 WHERE user_name='steven';
-- Make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);