2010-04-18 5 views
7

Bien que je me sers mySQL (pour l'instant), je ne veux pas que tout SQL spécifique DB.ANSI SQL question - comment insérer ou mettre à jour un dossier si elle existe déjà?

Je suis en train d'insérer un enregistrement si elle n'existe pas, et mettre à jour un champ si elle existe. Je veux utiliser ANSI SQL.

Le tableau ressemble à ceci:

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) 

Répondre

7

Pendant longtemps, cette opération nécessitait deux commandes distinctes, plus un certain cadre pour y faire face. D'où le nom UPSERT (UPdate ou inSERT). Mais des versions plus récentes de certaines versions de SGBD supportent des solutions plus élégantes.

La norme ANSI définit a MERGE syntax. Cela a été pris en charge dans Oracle depuis la version 9i et dans MS SQL Server depuis 2005. Les instructions MERGE peuvent être quelque peu verbeuses.

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) 
/

Je pense que la déclaration MERGE est principalement envisagée comme un outil de migration de données, de sorte que ses exigences de syntaxe que nous sélectionnons les données d'une table, dans la clause USING. nous pouvons contourner cette limitation en sélectionnant des littéraux et des pseudo-colonnes à partir d'un périphérique générateur de ligne (tel que dual dans Oracle).

MySQL a une syntaxe sightly différente, INSERT ... ON DUPLICATE KEY UPDATE.

+0

J'ai fini par utiliser la commande INSERT .. ON DUPLICATE KEY UPDATE – morpheous

+4

MS SQL Server 2005 ne prend pas en charge la syntaxe MERGE. Seul SQL Server 2008 et les versions supérieures le prennent en charge. –

+1

On dirait que sql server 2005 beta l'a eu mais pas la version finale. 2008 l'a eu. Référence: http://geekswithblogs.net/SabotsShell/archive/2005/08/20/50706.aspx – Alex

1

Ceci est défini dans SQL3 comme MERGE.

+0

solution Grande, dommage que beaucoup de bases de données ne supportent pas encore :( – Wolph

+0

SQL Server 2005 prend en charge merge, tout comme Oracle 9i (et plus) – APC

+0

SQL 2005 ne pas fusion de soutien, il a été introduit dans SQL 2008 –

1

Utilisez une UPSERT paire de commandes:

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

et

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

ce serait Oracle spécifique, non? – Wolph

+1

non: vous pouvez aussi utiliser la table double dans mySql. Et dans SQL Server, vous pouvez l'omettre complètement si tout ce dont vous avez besoin est SELECT . – davek

0

Une façon de le faire est d'exécuter simplement une commande d'insertion et de mise à jour, en ignorant l'erreur sur le premier s'il y a déjà un enregistrement avec cette clé:

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 vous voulez l'initiale poids d'une entrée créée pour être (par exemple) 72, l'utiliser comme la première déclaration:

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

Une approche conforme aux normes SQL anciennes et donc compatible avec une gamme plus large de SGBD (à partir de maintenant SQLite, par exemple, does not support MERGE) est d'utiliser a technique involving a mutex table:

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

qui permet l'émulation d'un INSERT SINON EXISTE déclaration:

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; 

Dans le cas de la question de l'OP qui serait suivie par une simple MISE À JOUR:

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

En fait, je poste ceci pour ma propre référence parce que la source que j'avais (xaprb.com) ne se présente pas facilement sur les recherches Google :) –

+2

J'aime votre solution, car elle devrait fonctionner sur presque tous les SGBD. Mais j'ai trouvé une petite amélioration. Vous n'avez pas besoin de la table mutex. Faites simplement une sous-sélection pour que 'FROM mutex LEFT JOIN test_table' devienne' FROM (SELECT 0 AS i) AS mutex LEFT JOIN test_table' – JHoffmann

Questions connexes