Estilo de preguntas y respuestas
Bueno, después de investigar y luchar con el problema durante horas, descubrí que hay dos formas de lograr esto, dependiendo de la estructura de su tabla y si tiene restricciones de claves externas activadas para mantener la integridad. Me gustaría compartir esto en un formato limpio para ahorrar algo de tiempo a las personas que puedan estar en mi situación.
Opción 1: puede permitirse eliminar la fila
En otras palabras, no tiene una clave externa, o si las tiene, su motor SQLite está configurado para que no haya excepciones de integridad. El camino a seguir es INSERTAR O REEMPLAZAR . Si está intentando insertar / actualizar un reproductor cuya ID ya existe, el motor SQLite eliminará esa fila e insertará los datos que está proporcionando. Ahora surge la pregunta: ¿qué hacer para mantener asociada la antigua ID?
Digamos que queremos UPSERT con los datos user_name = 'steven' y age = 32.
Mira 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)
El truco está en la fusión. Devuelve la identificación del usuario 'steven' si la hay, y de lo contrario, devuelve una nueva identificación nueva.
Opción 2: no puede permitirse eliminar la fila
Después de jugar con la solución anterior, me di cuenta de que en mi caso eso podría terminar destruyendo datos, ya que este ID funciona como una clave externa para otra tabla. Además, creé la tabla con la cláusula ON DELETE CASCADE , lo que significaría que eliminaría los datos en silencio. Peligroso.
Entonces, primero pensé en una cláusula IF, pero SQLite solo tiene CASE . Y este CASE no se puede usar (o al menos no lo administré) para realizar una consulta de ACTUALIZACIÓN si EXISTE (seleccione la identificación de los jugadores donde user_name = 'steven'), e INSERT si no lo hizo. No vayas.
Y luego, finalmente usé la fuerza bruta, con éxito. La lógica es que, para cada UPSERT que desee realizar, primero ejecute INSERT OR IGNORE para asegurarse de que haya una fila con nuestro usuario, y luego ejecute una consulta UPDATE con exactamente los mismos datos que intentó insertar.
Los mismos datos que antes: user_name = 'steven' y age = 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';
¡Y eso es todo!
EDITAR
Como ha comentado Andy, intentar insertar primero y luego actualizar puede provocar que se activen los activadores con más frecuencia de lo esperado. En mi opinión, esto no es un problema de seguridad de los datos, pero es cierto que disparar eventos innecesarios tiene poco sentido. Por tanto, una solución mejorada sería:
-- 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);