SQLite - UPSERT * no * INSERTAR o REEMPLAZAR

http://en.wikipedia.org/wiki/Upsert

Insertar actualización almacenada proc en SQL Server

¿Hay alguna forma inteligente de hacer esto en SQLite en la que no haya pensado?

Básicamente, quiero actualizar tres de las cuatro columnas si el registro existe. Si no existe, quiero INSERTAR el registro con el valor predeterminado (NUL) para la cuarta columna.

El ID es una clave principal, por lo que solo habrá un registro para UPSERT.

(Estoy tratando de evitar la sobrecarga de SELECT para determinar si necesito ACTUALIZAR o INSERTAR obviamente)

Sugerencias


No puedo confirmar esa sintaxis en el sitio SQLite para TABLE CREATE. No he creado una demostración para probarlo, pero parece que no es compatible.

Si lo fuera, tengo tres columnas, por lo que en realidad se vería así:

CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    Blob1 BLOB ON CONFLICT REPLACE, 
    Blob2 BLOB ON CONFLICT REPLACE, 
    Blob3 BLOB 
);

pero los dos primeros blobs no causarán un conflicto, solo el ID lo haría, así que asusme Blob1 y Blob2 no serían reemplazados (como se desea)


ACTUALIZACIONES en SQLite cuando los datos vinculados son una transacción completa, lo que significa que cada fila enviada para actualizar requiere: Preparar / Vincular / Paso / Finalizar declaraciones a diferencia de INSERT, que permite el uso de la función de reinicio

La vida de un objeto de declaración es algo como esto:

  1. Cree el objeto usando sqlite3_prepare_v2 ()
  2. Vincular valores a los parámetros del host mediante interfaces sqlite3_bind_.
  3. Ejecute el SQL llamando a sqlite3_step ()
  4. Restablezca la declaración usando sqlite3_reset () luego regrese al paso 2 y repita.
  5. Destruya el objeto de declaración usando sqlite3_finalize ().

ACTUALIZACIÓN Supongo que es lento en comparación con INSERT, pero ¿cómo se compara con SELECT usando la clave principal?

¿Quizás debería usar la selección para leer la cuarta columna (Blob4) y luego usar REPLACE para escribir un nuevo registro que combine la cuarta columna original con los nuevos datos de las primeras 3 columnas?

preguntado el 06 de enero de 09 a las 23:01

SQLite - UPSERT disponible en versión preliminar consulte: sqlite.1065341.n5.nabble.com/… -

UPSERT disponible en la versión 3.24.0 de SQLite -

18 Respuestas

Asumiendo tres columnas en la tabla: ID, NAME, ROLE


MALO: Esto insertará o reemplazará todas las columnas con nuevos valores para ID = 1:

INSERT OR REPLACE INTO Employee (id, name, role) 
  VALUES (1, 'John Foo', 'CEO');

MALO: Esto insertará o reemplazará 2 de las columnas ... la columna NAME se establecerá en NULL o en el valor predeterminado:

INSERT OR REPLACE INTO Employee (id, role) 
  VALUES (1, 'code monkey');

BUENA: Use SQLite On cláusula de conflicto Soporte UPSERT en SQLite! ¡La sintaxis UPSERT se agregó a SQLite con la versión 3.24.0!

UPSERT es una adición de sintaxis especial a INSERT que hace que INSERT se comporte como una ACTUALIZACIÓN o una operación no operativa si INSERT viola una restricción de unicidad. UPSERT no es SQL estándar. UPSERT en SQLite sigue la sintaxis establecida por PostgreSQL.

enter image description here

BUENO pero tendous: Esto actualizará 2 de las columnas. Cuando existe ID = 1, el NOMBRE no se verá afectado. Cuando ID = 1 no existe, el nombre será el predeterminado (NULL).

INSERT OR REPLACE INTO Employee (id, role, name) 
  VALUES (  1, 
            'code monkey',
            (SELECT name FROM Employee WHERE id = 1)
          );

Esto actualizará 2 de las columnas. Cuando existe ID = 1, el ROLE no se verá afectado. Cuando ID = 1 no existe, la función se establecerá en 'Benchwarmer' en lugar del valor predeterminado.

INSERT OR REPLACE INTO Employee (id, name, role) 
  VALUES (  1, 
            'Susan Bar',
            COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
          );

Respondido el 18 de diciembre de 19 a las 10:12

+1 brillante! La cláusula de selección incrustada le brinda la flexibilidad de anular la funcionalidad predeterminada ON CONFLICT REPLACE si necesita combinar / comparar el valor anterior y el nuevo valor para cualquier campo. - GRAMO__

Si otras filas hacen referencia al empleado con eliminación en cascada, las otras filas aún se eliminarán por reemplazo. - Don reba

La última consulta no es correcta. Debería ser: coalesce ((seleccione el rol de Empleado donde id = 1), 'Benchwarmer') - Viet

¿Podrías explicar por qué? Esto insertará o reemplazará todas las columnas con nuevos valores para ID = 1: es considerado BAD en tu primer ejemplo? El comando que presenta allí está destinado a crear un nuevo registro con ID 1, nombre John Foo y rol CEO, o sobrescriba el registro cuyo ID es 1, si ya está allí, con esos datos (asumiendo el id es la clave principal). Entonces, ¿por qué es malo si eso sucede exactamente? - OR Mapper

@Cornelius: Eso está claro, pero eso no es lo que sucede en el primer ejemplo. El primer ejemplo está destinado a establecer todas las columnas a la fuerza, que es exactamente lo que sucede, sin importar si el registro se inserta o se reemplaza. Entonces, ¿por qué eso se considera malo? La respuesta vinculada también solo señala por qué puede suceder algo malo al especificar un subconjunto de las columnas, como en su segundo ejemplo; no parece dar más detalles sobre los efectos negativos de lo que sucede en su en el primer ejemplo, INSERT OR REPLACE al especificar valores para todos columnas. - OR Mapper

INSERT OR REEMPLAZAR es NO equivalente a "UPSERT".

Digamos que tengo la tabla Empleado con los campos id, nombre y rol:

INSERT OR REPLACE INTO Employee ("id", "name", "role") VALUES (1, "John Foo", "CEO")
INSERT OR REPLACE INTO Employee ("id", "role") VALUES (1, "code monkey")

Boom, ha perdido el nombre del empleado número 1. SQLite lo ha reemplazado con un valor predeterminado.

El resultado esperado de un UPSERT sería cambiar el rol y mantener el nombre.

Respondido el 16 de enero de 12 a las 18:01

Pero es cierto entonces que INSERT OR REPLACE en una tabla con dos columnas (y sin nulos) es equivalente a UPSERT. - QED

De acuerdo, no es 100% positivo, pero puede gustarle en algunos casos como el anterior. Por lo tanto, el programador deberá hacer su propio juicio. - Gilco

@QED no, porque eliminar + insertar (que es un reemplazo) son 2 declaraciones dml, con sus propios desencadenantes, por ejemplo. No es lo mismo que 1 declaración de actualización solamente. - Sebas

La respuesta de Eric B está bien si desea conservar solo una o quizás dos columnas de la fila existente. Si desea conservar muchas columnas, se vuelve demasiado engorroso rápidamente.

Este es un enfoque que se adaptará bien a cualquier cantidad de columnas en ambos lados. Para ilustrarlo asumiré el siguiente esquema:

 CREATE TABLE page (
     id      INTEGER PRIMARY KEY,
     name    TEXT UNIQUE,
     title   TEXT,
     content TEXT,
     author  INTEGER NOT NULL REFERENCES user (id),
     ts      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
 );

Tenga en cuenta en particular que name es la clave natural de la fila - id se usa solo para claves externas, por lo que el punto es que SQLite elija el valor de ID cuando inserte una nueva fila. Pero al actualizar una fila existente en función de su name, Quiero que siga teniendo el valor de ID anterior (¡obviamente!).

Logro un verdadero UPSERT con la siguiente construcción:

 WITH new (name, title, author) AS ( VALUES('about', 'About this site', 42) )
 INSERT OR REPLACE INTO page (id, name, title, content, author)
 SELECT old.id, new.name, new.title, old.content, new.author
 FROM new LEFT JOIN page AS old ON new.name = old.name;

La forma exacta de esta consulta puede variar un poco. La clave es el uso de INSERT SELECT con una combinación externa izquierda, para unir una fila existente a los nuevos valores.

Aquí, si una fila no existía previamente, old.id se mostrarán NULL y SQLite asignará una ID automáticamente, pero si ya existía dicha fila, old.id tendrá un valor real y será reutilizado. Que es exactamente lo que quería.

De hecho, esto es muy flexible. Note como el ts La columna está completamente ausente en todos los lados, porque tiene una DEFAULT valor, SQLite simplemente hará lo correcto en cualquier caso, por lo que no tengo que ocuparme de ello yo mismo.

También puede incluir una columna en ambos new y old lados y luego use, por ejemplo, COALESCE(new.content, old.content) en el exterior SELECT para decir "inserte el contenido nuevo si lo hubiera, de lo contrario, mantenga el contenido anterior", por ejemplo, si está utilizando una consulta fija y está vinculando los nuevos valores con marcadores de posición.

contestado el 23 de mayo de 17 a las 12:05

+1, funciona muy bien, pero agrega un WHERE name = "about" restricción en el SELECT ... AS old para acelerar las cosas. Si tiene más de 1 m de filas, esto es muy lento. - usuario918938

Buen punto, +1 en tu comentario. Sin embargo, dejaré eso fuera de la respuesta, porque agregar tal WHERE La cláusula requiere solo el tipo de redundancia en la consulta que estaba tratando de obviar en primer lugar cuando se me ocurrió este enfoque. Como siempre: cuando necesite rendimiento, desnormalice, en este caso, la estructura de la consulta. - Aristóteles Pagaltzis

Puede simplificar el ejemplo de Aristóteles a esto, si lo desea: INSERT OR REPLACE INTO page (id, name, title, content, author) SELECT id, 'about', 'About this site', content, 42 FROM ( SELECT NULL ) LEFT JOIN ( SELECT * FROM page WHERE name = 'about' ) - jcox

¿No desencadenaría esto innecesariamente ON DELETE se activa cuando realiza un reemplazo (es decir, una actualización)? - Karakuri

Sin duda desencadenará ON DELETE desencadenantes. No sé innecesariamente. Para la mayoría de los usuarios, probablemente sería innecesario, incluso no deseado, pero tal vez no para todos los usuarios. Del mismo modo, por el hecho de que también eliminará en cascada cualquier fila con claves externas en la fila en cuestión, probablemente un problema para muchos usuarios. SQLite no tiene nada más parecido a un UPSERT real, desafortunadamente. (Salvo por fingir con un INSTEAD OF UPDATE disparador, supongo.) - Aristóteles Pagaltzis

Si generalmente está haciendo actualizaciones, lo haría ...

  1. Iniciar una transacción
  2. Haz la actualización
  3. Verifique el recuento de filas
  4. Si es 0, inserte
  5. COMPROMÉTASE

Si generalmente está haciendo inserciones, lo haría

  1. Iniciar una transacción
  2. Prueba un inserto
  3. Verifique el error de violación de la clave principal
  4. si tenemos un error, haz la actualización
  5. COMPROMÉTASE

De esta manera, evita la selección y está transaccionalmente sólido en Sqlite.

Respondido el 07 de enero de 09 a las 02:01

Si va a verificar el recuento de filas usando sqlite3_changes () en el tercer paso, asegúrese de no usar el identificador de base de datos de varios subprocesos para modificaciones. - Linulin

¿No sería lo siguiente menos prolijo pero con el mismo efecto: 1) seleccione la tabla de formulario de identificación donde id = 'x' 2) si (ResultSet.rows.length == 0) actualice la tabla donde id = 'x'; - florín

¿Cómo funcionaría esto en la inserción / actualización masiva? Sin embargo, está bien para uno a la vez. - Michael Gabay

Esta respuesta se ha actualizado y, por lo tanto, los comentarios a continuación ya no se aplican.

2018-05-18 DETENER PULSAR.

Soporte UPSERT en SQLite! ¡La sintaxis UPSERT se agregó a SQLite con la versión 3.24.0 (pendiente)!

UPSERT es una adición de sintaxis especial a INSERT que hace que INSERT se comporte como una ACTUALIZACIÓN o una operación no operativa si INSERT viola una restricción de unicidad. UPSERT no es SQL estándar. UPSERT en SQLite sigue la sintaxis establecida por PostgreSQL.

enter image description here

alternativamente:

Otra forma completamente diferente de hacer esto es: En mi aplicación, configuro mi ID de fila en la memoria para que sea long.MaxValue cuando creo la fila en la memoria. (MaxValue nunca se usará como ID, no vivirá lo suficiente ... Entonces, si rowID no es ese valor, entonces ya debe estar en la base de datos, por lo que necesita una ACTUALIZACIÓN si es MaxValue, entonces necesita una inserción. Esto solo es útil si puede realizar un seguimiento de los ID de fila en su aplicación.

Respondido 23 Abr '19, 09:04

INSERT INTO table(...) SELECT ... WHERE changes() = 0; funciona para mi. - Manuel Lopera

Agradable y simple, pero tiene una condición de carrera cuando se elimina la fila entre la actualización y la inserción, ¿no? - w00t

@ w00t ¿Qué pasa si ejecuta esto dentro de una transacción? - copolii

@copolii en realidad no está seguro de si eso es suficiente, ¿quizás debería bloquear la fila? - w00t

Así es como funciona el módulo nodejs sqlite-upsert. github.com/pbrandt1/sqlite3-upsert/blob/master/index.js - fiat

Me doy cuenta de que este es un hilo antiguo, pero he estado trabajando en sqlite3 últimamente y se me ocurrió este método que se adapta mejor a mis necesidades de generar consultas parametrizadas dinámicamente:

insert or ignore into <table>(<primaryKey>, <column1>, <column2>, ...) values(<primaryKeyValue>, <value1>, <value2>, ...); 
update <table> set <column1>=<value1>, <column2>=<value2>, ... where changes()=0 and <primaryKey>=<primaryKeyValue>; 

Siguen siendo 2 consultas con una cláusula where en la actualización, pero parece que funcionan. También tengo esta visión en mi cabeza de que sqlite puede optimizar la declaración de actualización por completo si la llamada a changes () es mayor que cero. Si realmente lo hace o no, está más allá de mi conocimiento, pero un hombre puede soñar, ¿no? ;)

Para obtener puntos de bonificación, puede agregar esta línea que le devuelve la identificación de la fila, ya sea una fila recién insertada o una fila existente.

select case changes() WHEN 0 THEN last_insert_rowid() else <primaryKeyValue> end;

Respondido el 08 de Septiembre de 11 a las 20:09

Aquí hay una solución que realmente es un UPSERT (ACTUALIZAR o INSERTAR) en lugar de INSERTAR O REEMPLAZAR (que funciona de manera diferente en muchas situaciones).

Funciona así:
1. Intente actualizar si existe un registro con el mismo Id.
2. Si la actualización no cambió ninguna fila (NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0)), luego inserte el registro.

Por lo tanto, se actualizó un registro existente o se realizará una inserción.

El detalle importante es usar la función de SQL changes () para verificar si la declaración de actualización alcanzó algún registro existente y solo realizar la declaración de inserción si no alcanzó ningún registro.

Una cosa a mencionar es que la función changes () no devuelve los cambios realizados por disparadores de nivel inferior (ver http://sqlite.org/lang_corefunc.html#changes), así que asegúrese de tenerlo en cuenta.

Aquí está el SQL ...

Actualización de prueba:

--Create sample table and records (and drop the table if it already exists)
DROP TABLE IF EXISTS Contact;
CREATE TABLE [Contact] (
  [Id] INTEGER PRIMARY KEY, 
  [Name] TEXT
);
INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
INSERT INTO Contact (Id, Name) VALUES (2, 'John');

-- Try to update an existing record
UPDATE Contact
SET Name = 'Bob'
WHERE Id = 2;

-- If no record was changed by the update (meaning no record with the same Id existed), insert the record
INSERT INTO Contact (Id, Name)
SELECT 2, 'Bob'
WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);

--See the result
SELECT * FROM Contact;

Inserto de prueba:

--Create sample table and records (and drop the table if it already exists)
DROP TABLE IF EXISTS Contact;
CREATE TABLE [Contact] (
  [Id] INTEGER PRIMARY KEY, 
  [Name] TEXT
);
INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
INSERT INTO Contact (Id, Name) VALUES (2, 'John');

-- Try to update an existing record
UPDATE Contact
SET Name = 'Bob'
WHERE Id = 3;

-- If no record was changed by the update (meaning no record with the same Id existed), insert the record
INSERT INTO Contact (Id, Name)
SELECT 3, 'Bob'
WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);

--See the result
SELECT * FROM Contact;

Respondido 27 Feb 14, 22:02

Esta parece ser la mejor solución para mí que las de Eric. sin emabargo INSERT INTO Contact (Id, Name) SELECT 3, 'Bob' WHERE changes() = 0; también debería funcionar. - bkausbk

A partir de la versión 3.24.0 UPSERT es compatible con SQLite.

Desde el documentación:

UPSERT es una adición de sintaxis especial a INSERT que hace que INSERT se comporte como una ACTUALIZACIÓN o una operación no operativa si INSERT viola una restricción de unicidad. UPSERT no es SQL estándar. UPSERT en SQLite sigue la sintaxis establecida por PostgreSQL. La sintaxis UPSERT se agregó a SQLite con la versión 3.24.0 (pendiente).

Un UPSERT es una declaración INSERT ordinaria seguida de la cláusula especial ON CONFLICT

enter image description here

Fuente de imagen: https://www.sqlite.org/images/syntax/upsert-clause.gif

Respondido el 20 de junio de 20 a las 10:06

Android todavía está en 3.19 a partir de API 27 - Brill Pappin

Actualizaciones de Bernhardt:

De hecho, puede hacer una inserción en SQLite, simplemente se ve un poco diferente de lo que está acostumbrado. Se vería algo así como:

INSERT INTO table_name (id, column1, column2) 
VALUES ("youruuid", "value12", "value2")
ON CONFLICT(id) DO UPDATE 
SET column1 = "value1", column2 = "value2"

respondido 20 nov., 20:19

Esto es realmente incorrecto. Debería ser así: INSERT INTO table_name (id, column1, column2) VALUES (123, 'value1', 'value2') ON CONFLICT (id) DO UPDATE SET column1 = 'value1', column2 = 'value2' - bernhardt scherer

Sí, ya veo, el inserto también necesita la identificación. Lo modificaré para que sea más correcto. También como el conflicto en id. - Brill Pappin

Ampliando en La respuesta de Aristóteles puede SELECCIONAR de una tabla ficticia 'singleton' (una tabla de su propia creación con una sola fila). Esto evita algunas duplicaciones.

También mantuve el ejemplo portátil en MySQL y SQLite y usé una columna 'date_added' como un ejemplo de cómo se puede establecer una columna solo la primera vez.

 REPLACE INTO page (
   id,
   name,
   title,
   content,
   author,
   date_added)
 SELECT
   old.id,
   "about",
   "About this site",
   old.content,
   42,
   IFNULL(old.date_added,"21/05/2013")
 FROM singleton
 LEFT JOIN page AS old ON old.name = "about";

contestado el 23 de mayo de 17 a las 13:05

El mejor enfoque que conozco es hacer una actualización, seguida de una inserción. La "sobrecarga de una selección" es necesaria, pero no es una carga terrible ya que está buscando en la clave principal, que es rápida.

Debería poder modificar las siguientes declaraciones con los nombres de su tabla y campo para hacer lo que quiera.

--first, update any matches
UPDATE DESTINATION_TABLE DT
SET
  MY_FIELD1 = (
              SELECT MY_FIELD1
              FROM SOURCE_TABLE ST
              WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
              )
 ,MY_FIELD2 = (
              SELECT MY_FIELD2
              FROM SOURCE_TABLE ST
              WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
              )
WHERE EXISTS(
            SELECT ST2.PRIMARY_KEY
            FROM
              SOURCE_TABLE ST2
             ,DESTINATION_TABLE DT2
            WHERE ST2.PRIMARY_KEY = DT2.PRIMARY_KEY
            );

--second, insert any non-matches
INSERT INTO DESTINATION_TABLE(
  MY_FIELD1
 ,MY_FIELD2
)
SELECT
  ST.MY_FIELD1
 ,NULL AS MY_FIELD2  --insert NULL into this field
FROM
  SOURCE_TABLE ST
WHERE NOT EXISTS(
                SELECT DT2.PRIMARY_KEY
                FROM DESTINATION_TABLE DT2
                WHERE DT2.PRIMARY_KEY = ST.PRIMARY_KEY
                );

Respondido el 07 de enero de 09 a las 02:01

Creo que no es una buena idea porque necesita hacer dos solicitudes al motor de la base de datos. - Ricardo da Rocha Víctor

Si alguien quiere leer mi solución para SQLite en Cordova, obtuve este método js genérico gracias a la respuesta de @david anterior.

function    addOrUpdateRecords(tableName, values, callback) {
get_columnNames(tableName, function (data) {
    var columnNames = data;
    myDb.transaction(function (transaction) {
        var query_update = "";
        var query_insert = "";
        var update_string = "UPDATE " + tableName + " SET ";
        var insert_string = "INSERT INTO " + tableName + " SELECT ";
        myDb.transaction(function (transaction) {
            // Data from the array [[data1, ... datan],[()],[()]...]:
            $.each(values, function (index1, value1) {
                var sel_str = "";
                var upd_str = "";
                var remoteid = "";
                $.each(value1, function (index2, value2) {
                    if (index2 == 0) remoteid = value2;
                    upd_str = upd_str + columnNames[index2] + "='" + value2 + "', ";
                    sel_str = sel_str + "'" + value2 + "', ";
                });
                sel_str = sel_str.substr(0, sel_str.length - 2);
                sel_str = sel_str + " WHERE NOT EXISTS(SELECT changes() AS change FROM "+tableName+" WHERE change <> 0);";
                upd_str = upd_str.substr(0, upd_str.length - 2);
                upd_str = upd_str + " WHERE remoteid = '" + remoteid + "';";                    
                query_update = update_string + upd_str;
                query_insert = insert_string + sel_str;  
                // Start transaction:
                transaction.executeSql(query_update);
                transaction.executeSql(query_insert);                    
            });
        }, function (error) {
            callback("Error: " + error);
        }, function () {
            callback("Success");
        });
    });
});
}

Entonces, primero elija los nombres de las columnas con esta función:

function get_columnNames(tableName, callback) {
myDb.transaction(function (transaction) {
    var query_exec = "SELECT name, sql FROM sqlite_master WHERE type='table' AND name ='" + tableName + "'";
    transaction.executeSql(query_exec, [], function (tx, results) {
        var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); ///// RegEx
        var columnNames = [];
        for (i in columnParts) {
            if (typeof columnParts[i] === 'string')
                columnNames.push(columnParts[i].split(" ")[0]);
        };
        callback(columnNames);
    });
});
}

Luego, cree las transacciones mediante programación.

"Valores" es una matriz que debe crear antes y representa las filas que desea insertar o actualizar en la tabla.

"remoteid" es la identificación que utilicé como referencia, ya que estoy sincronizando con mi servidor remoto.

Para el uso del complemento SQLite Cordova, consulte el aquí

Respondido el 31 de enero de 17 a las 15:01

Este método mezcla algunos de los otros métodos de la respuesta para esta pregunta e incorpora el uso de CTE (Expresiones de tabla comunes). Presentaré la consulta y luego explicaré por qué hice lo que hice.

Me gustaría cambiar el apellido del empleado 300 a DAVIS si hay un empleado 300. De lo contrario, agregaré un nuevo empleado.

Nombre de la tabla: empleados Columnas: id, first_name, last_name

La consulta es:

INSERT OR REPLACE INTO employees (employee_id, first_name, last_name)
WITH registered_employees AS ( --CTE for checking if the row exists or not
    SELECT --this is needed to ensure that the null row comes second
        *
    FROM (
        SELECT --an existing row
            *
        FROM
            employees
        WHERE
            employee_id = '300'

        UNION

        SELECT --a dummy row if the original cannot be found
            NULL AS employee_id,
            NULL AS first_name,
            NULL AS last_name
    )
    ORDER BY
        employee_id IS NULL --we want nulls to be last
    LIMIT 1 --we only want one row from this statement
)
SELECT --this is where you provide defaults for what you would like to insert
    registered_employees.employee_id, --if this is null the SQLite default will be used
    COALESCE(registered_employees.first_name, 'SALLY'),
    'DAVIS'
FROM
    registered_employees
;

Básicamente, utilicé el CTE para reducir la cantidad de veces que se debe usar la instrucción de selección para determinar los valores predeterminados. Dado que se trata de un CTE, simplemente seleccionamos las columnas que queremos de la tabla y la instrucción INSERT usa esto.

Ahora puede decidir qué valores predeterminados desea usar reemplazando los nulos, en la función COALESCE con cuáles deberían ser los valores.

Respondido el 29 de enero de 16 a las 06:01

Siguiendo Aristóteles Pagaltzis y la idea de COALESCE de La respuesta de Eric B, aquí es una opción de actualización para actualizar solo algunas columnas o insertar una fila completa si no existe.

En este caso, imagine que el título y el contenido deben actualizarse, manteniendo los otros valores antiguos cuando existen e insertando los proporcionados cuando no se encuentra el nombre:

NOTA id se fuerza a ser NULL cuando INSERT ya que se supone que es autoincremento. Si es solo una clave primaria generada, entonces COALESCE también se puede utilizar (ver Comentario de Aristóteles Pagaltzis).

WITH new (id, name, title, content, author)
     AS ( VALUES(100, 'about', 'About this site', 'Whatever new content here', 42) )
INSERT OR REPLACE INTO page (id, name, title, content, author)
SELECT
     old.id, COALESCE(old.name, new.name),
     new.title, new.content,
     COALESCE(old.author, new.author)
FROM new LEFT JOIN page AS old ON new.name = old.name;

Entonces, la regla general sería, si desea mantener los valores antiguos, use COALESCE, cuando desee actualizar los valores, utilice new.fieldname

Respondido el 03 de enero de 18 a las 17:01

COALESCE(old.id, new.id) definitivamente está mal con una clave de autoincremento. Y aunque “mantener la mayor parte de la fila sin cambios, excepto donde faltan valores” suena como un caso de uso que alguien podría tener, no creo que eso sea lo que la gente busca cuando busca cómo hacer un UPSERT. - Aristóteles Pagaltzis

@AristotlePagaltzis se disculpa si me equivoco, no estoy usando aumentos automáticos. Lo que busco con esta consulta es actualice solo algunas columnas o inserte una fila completa con los valores proporcionados en caso de que no exista. He estado jugando con su consulta y no pude lograrlo al insertar: las columnas seleccionadas de old mesa donde asignada a NULL, no a los valores suministrados en new. Esta es la razón para usar COALESCE. No soy un experto en sqlite, he estado probando esta consulta y parece funcionar para el caso, le agradecería mucho si pudiera indicarme la solución con aumentos automáticos. Miquel

En caso de una clave de autoincremento, quieres para insertar NULL como clave, porque eso le dice a SQLite que inserte el siguiente valor disponible. - Aristóteles Pagaltzis

No estoy diciendo que no debas hacer lo que estás haciendo (si lo necesitas, entonces lo necesitas), solo que no es realmente una respuesta a la pregunta aquí. UPSERT generalmente significa que tiene una fila que desea almacenar en la tabla y simplemente no sabe si ya tiene una fila coincidente en la que poner los valores o si necesita insertarlos como una nueva fila. Su caso de uso es que si ya tiene una fila coincidente, desea ignorar la mayoría de los valores de la nueva fila. Está bien, simplemente no es la pregunta que se hizo. - Aristóteles Pagaltzis

Creo que esto puede ser lo que estás buscando: EN CONFLICTO cláusula.

Si define su tabla así:

CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    field1 TEXT 
); 

Ahora, si hace un INSERT con una identificación que ya existe, SQLite automáticamente ACTUALIZA en lugar de INSERT.

Hth ...

Respondido el 07 de enero de 09 a las 02:01

No creo que esto funcione, eliminará las columnas que faltan en la declaración de inserción: sam azafrán

@Mosor: -1 de mi parte, lo siento. Esto es lo mismo que emitir un REPLACE declaración. - Alix Axel

-1 porque esto hace una eliminación y luego una inserción si la clave principal ya existe. - rápido

Si no le importa hacer esto en dos operaciones.

Pasos:

1) Agregue nuevos elementos con "INSERT OR IGNORE"

2) Actualice los elementos existentes con "ACTUALIZAR"

La entrada a ambos pasos es la misma colección de elementos nuevos o actualizables. Funciona bien con elementos existentes que no necesitan cambios. Se actualizarán, pero con los mismos datos y por lo tanto el resultado neto es sin cambios.

Seguro, más lento, etc. Ineficiente. Sí.

¿Es fácil escribir el sql, mantenerlo y comprenderlo? Definitivamente.

Es una compensación a considerar. Funciona muy bien para pequeñas afirmaciones. Funciona muy bien para aquellos a los que no les importa sacrificar la eficiencia por el mantenimiento del código.

Respondido el 05 de enero de 20 a las 23:01

Nota para todos: INSERTAR O REEMPLAZAR NO es de lo que se trata esta publicación. INSERT OR REPLACE creará una NUEVA fila en su tabla, con una nueva ID. Eso no es una ACTUALIZACIÓN. - balde de savia

Después de leer este hilo y me decepcionó que no fuera fácil simplemente hacer este "UPSERT", investigué más ...

De hecho, puede hacer esto directa y fácilmente en SQLITE.

En lugar de usar: INSERT INTO

Uso: INSERT OR REPLACE INTO

¡Esto hace exactamente lo que quieres que haga!

Respondido el 26 de diciembre de 11 a las 18:12

-1 INSERT OR REPLACE no es un UPSERT. Vea "respuesta" de gregschlom por la razón por la cual. La solución de Eric B realmente funciona y necesita algunos votos a favor. - Día

SELECT COUNT(*) FROM table1 WHERE id = 1;

if COUNT(*) = 0

INSERT INTO table1(col1, col2, cole) VALUES(var1,var2,var3);

Si no si COUNT(*) > 0

UPDATE table1 SET col1 = var4, col2 = var5, col3 = var6 WHERE id = 1;

contestado el 02 de mayo de 14 a las 04:05

Esto es demasiado complicado, SQL puede manejar esto bien en una consulta: Robin Kanters

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.