2010-04-18 23 views
7

Aunque estoy usando mySQL (por ahora), no quiero ningún SQL específico de DB.Pregunta ANSI SQL: ¿cómo insertar o actualizar un registro si ya existe?

Estoy tratando de insertar un registro si no existe, y actualizo un campo si existe. Quiero usar ANSI SQL.

La tabla es como la siguiente:

create table test_table (id int, name varchar(16), weight double) ; 

//test data 
insert into test_table (id, name, weight) values(1,'homer', 900); 
insert into test_table (id, name, weight) values(2,'marge', 85); 
insert into test_table (id, name, weight) values(3,'bart', 25); 
insert into test_table (id, name, weight) values(4,'lisa', 15); 

If the record exists, I want to update the weight (increase by say 10) 

Respuesta

7

Durante mucho tiempo esta operación requiere dos comandos separados además de algunos marco para manejarlo. De ahí el nombre UPSERT (UPdate o inSERT). Pero las versiones más recientes de algunos sabores de DBMS soportan soluciones más elegantes.

El estándar ANSI define a MERGE syntax. Esto ha sido soportado en Oracle desde la versión 9i y en el servidor MS SQL desde 2005. Las sentencias MERGE pueden ser un tanto verbosas.

merge into t23 
using t42 
on t42.id = t23.id 
when matched then 
    update 
    set  t23.col1 = t42.col1 
when not matched then 
    insert (id, col1) 
    values (t42.id, t42.col1) 
/

creo que la instrucción MERGE se entiende primordialmente como una herramienta de migración de datos, por lo que sus demandas de sintaxis que seleccionamos datos de una tabla, en la cláusula USING. podemos evitar esta limitación seleccionando literales y pseudocolumnas desde un dispositivo generador de filas (como dual en Oracle).

MySQL tiene una sintaxis visualmente diferente, INSERT ... ON DUPLICATE KEY UPDATE.

+0

Terminé usando INSERT .. EN ACTUALIZACIÓN DE LLAVE DUPLICADA – morpheous

+4

MS SQL Server 2005 no es compatible con la sintaxis MERGE. Solo SQL Server 2008 y superior lo admite. –

+1

Parece que la versión beta del servidor sql 2005 lo tenía pero no la versión final. 2008 lo consiguió. Referencia: http://geekswithblogs.net/SabotsShell/archive/2005/08/20/50706.aspx – Alex

1

Esto se define en SQL3 como MERGE.

+0

gran solución, lástima que una gran cantidad de bases de datos no son compatibles con esto todavía :( – Wolph

+0

SQL Server 2005 admite la fusión, como se hace Oracle 9i (y superior) – APC

+0

SQL 2005 no lo hace fusión de soporte, se introdujo en SQL 2008 –

1

Utilice un UPSERT par de comandos:

update test_table inner join test_table lookup 
on test_table.id = lookup.id 
and lookup.name = 'bart' 
set test_table.colA = .... 

y

insert into test_table 
select 1,'xxx', 999 
from dual where exists <...> 
+0

Eso sería específico de Oracle, ¿verdad? – Wolph

+1

no: también puede usar la tabla doble en mySql. Y en SQL Server puede omitirlo por completo si todo lo que necesita es SELECCIONAR . – davek

0

Una forma de hacer esto es simplemente ejecutar un comando de inserción y actualización, ignorando el error en el primero si ya hay un registro con esa clave:

try: 
    insert into test_table (id, name, weight) values(1,'pax',0) 
catch (e): 
    pass 
update test_table set weight = weight * 1.1 where id = 1 

Si desea la inicial peso de una entrada creado para ser (por ejemplo) 72, utilice esto como la primera declaración:

insert into test_table (id, name, weight) values(1,'pax',72/1.1) 
2

Un enfoque compatible con los estándares SQL de edad avanzada y por lo tanto compatible con una gama más amplia de DBMS (a partir de ahora SQLite, por ejemplo, does not support MERGE) es utilizar a technique involving a mutex table:

CREATE TABLE mutex (i INT); 
INSERT INTO mutex VALUES (0); 

que permite la emulación de un Insertar si NO EXISTE declaración:

INSERT INTO test_table (id, name, weight) 
    SELECT 1, 'homer', 900 
    FROM mutex LEFT JOIN test_table 
    ON id = 1 
    WHERE i = 0 AND id IS NULL; 

En el caso de la pregunta de la OP que sería seguido mediante un simple UPDATE:

UPDATE test_table SET weight = weight + 10 WHERE id = 1; 
+0

En realidad estoy publicando esto para mi propia referencia porque la fuente que tuve (xaprb.com) no aparece fácilmente en las búsquedas de Google. :) –

+2

Me gusta su solución, porque debería funcionar en casi cualquier DBMS. Pero encontré una pequeña mejora. En realidad, no necesitas la tabla mutex. Solo haga una subselección para que 'FROM mutex IZQUIERDA UNIR test_table' se convierta en' FROM (SELECT 0 AS i) AS mutex IZQUIERDA UNIR test_table' – JHoffmann

Cuestiones relacionadas