2010-04-18 7 views
7

Obwohl ich mySQL (für jetzt) ​​verwende, möchte ich keine DB spezifische SQL.ANSI-SQL-Frage - wie fügt man einen Datensatz ein oder aktualisiert ihn, wenn er bereits existiert?

Ich versuche, einen Datensatz einzufügen, wenn es nicht existiert, und ein Feld zu aktualisieren, wenn es existiert. Ich möchte ANSI SQL verwenden.

Die Tabelle sieht etwa so aus:

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) 

Antwort

7

Für eine lange Zeit diese Operation zwei getrennte Befehle sowie einige Rahmen es zu handhaben erforderlich. Daher der Name UPSERT (UPdate oder inSERT). Aber neuere Versionen einiger Varianten von DBMS unterstützen elegantere Lösungen.

Der ANSI-Standard definiert a MERGE syntax. Dies wird in Oracle seit Version 9i und in MS SQL Server seit 2005 unterstützt. MERGE-Anweisungen können etwas ausführlich sein.

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

ich denke, die MERGE-Anweisung in erster Linie als Datenmigrations-Tool ins Auge gefasst wurde, so seine Syntax verlangt, dass wir Daten aus einer Tabelle auswählen, in der USING-Klausel. Wir können diese Einschränkung umgehen, indem wir Literale und Pseudo-Spalten von einem Zeilen erzeugenden Gerät auswählen (z. B. Dual in Oracle).

MySQL hat eine deutlich andere Syntax, INSERT ... ON DUPLICATE KEY UPDATE.

+0

Ich endete mit der INSERT .. ON DUPLICATE KEY UPDATE – morpheous

+4

MS SQL Server 2005 unterstützt nicht MERGE-Syntax. Nur SQL Server 2008 und höher unterstützen es. –

+1

Sieht aus wie SQL Server 2005 Beta hatte es aber nicht die endgültige Version. 2008 hat es geschafft. Referenz: http://geekswithblogs.net/SabotsShell/archive/2005/08/20/50706.aspx – Alex

1

Diese in SQL3 als MERGE definiert ist.

+0

Große Lösung, schade, dass viele Datenbanken :( – Wolph

+0

SQL Server 2005 unterstützt MERGE, ebenso wie Oracle 9i (und höher) – APC

+0

SQL 2005 diese noch nicht unterstützen, nicht Unterstützung merge, es wurde in SQL 2008 eingeführt –

1

Verwenden Sie ein UPSERT Paar Befehle:

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

und

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

Das wäre Oracle spezifisch, nicht wahr? – Wolph

+1

nein: Sie können auch die Dual-Tabelle in mySql verwenden. Und in SQL Server können Sie es ganz weglassen, wenn Sie nur SELECT benötigen. – davek

0

Eine Möglichkeit, dies zu tun, einen Einsatz und Update-Befehl einfach auszuführen ist, auf den ersten, die Fehler zu ignorieren, wenn es bereits ist ein Satz mit diesem Schlüssel:

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 

Wenn Sie die erste wollen Gewicht eines erzeugten Eintrag (zum Beispiel) zu sein, 72, verwenden, um dies als erste Anweisung:

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

Ein Ansatz kompatibel mit älteren SQL-Standards und damit mit einer breiteren Palette von DBMS kompatibel (ab jetzt SQLite zum Beispiel does not support MERGE) ist a technique involving a mutex table zu verwenden:

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

, dass die Emulation eines ermöglicht INSERT WENN NICHT VORHANDEN Anweisung:

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; 

Bei der Frage des OP, die wäre gefolgt durch ein einfaches UPDATE:

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

Eigentlich poste ich dies für meine eigene Referenz, weil die Quelle, die ich (xaprb.com) hatte, nicht leicht kommt bei Google-Suchen. :) –

+2

Ich mag deine Lösung, weil sie auf fast jedem DBMS funktionieren sollte. Aber ich habe eine kleine Verbesserung gefunden. Sie brauchen die Mutex-Tabelle eigentlich nicht. Machen Sie einfach einen Subselect, so dass 'FROM Mutex LEFT JOIN test_table' zu ​​'FROM (SELECT 0 AS i) AS Mutex LEFT JOIN test_table' wird – JHoffmann

Verwandte Themen