2010-04-18 43 views
7

儘管我使用mySQL(現在),但我不想要任何數據庫特定的SQL。ANSI SQL問題 - 如何插入或更新記錄(如果它已存在)?

我想插入一條記錄,如果它不存在,並更新一個字段,如果它確實存在。我想使用ANSI SQL。

表看起來是這樣的:

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) 

回答

7

長期以來需要兩個獨立的命令加上一些框架來處理它此操作。因此名稱UPSERT(更新或inSERT)。但是某些類型的DBMS的更新版本支持更優雅的解決方案。

ANSI標準定義了a MERGE syntax。從版本9i開始,Oracle自2005年起在MS SQL Server中支持此功能。MERGE語句可能有些冗長。

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

我認爲MERGE語句主要是設想作爲數據遷移工具,所以它的語法要求我們從表中選擇數據時,使用子句。我們可以通過從行生成設備中選擇文字列和僞列(例如Oracle中的雙列)來解決此限制。

MySQL有一個非常不同的語法,INSERT ... ON DUPLICATE KEY UPDATE

+0

我結束了使用INSERT ..在重複密鑰更新 – morpheous 2010-04-26 22:27:16

+4

MS SQL Server 2005不支持MERGE語法。只有SQL Server 2008和更高版本支持它。 – 2011-10-17 03:01:44

+1

看起來像SQL服務器2005測試版有它,但不是最終版本。 2008年得到它。參考:http://geekswithblogs.net/SabotsShell/archive/2005/08/20/50706.aspx – Alex 2012-11-14 15:37:30

1

這在SQL3定義爲MERGE

+0

的最佳解決方案,太糟糕了,有很多的數據庫不支持此尚未:( – Wolph 2010-04-18 14:04:49

+0

SQL Server 2005的支持融合,如同甲骨文9i中(或更高版本) – APC 2010-04-18 14:45:32

+0

SQL 2005不支持合併,它是在SQL 2008中引入的 – 2012-03-01 18:38:38

1

使用UPSERT對命令:這樣做是簡單地執行INSERT和UPDATE命令,忽略第一個錯誤是否已經存在的

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

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

這將是Oracle特有的,對吧? – Wolph 2010-04-18 14:03:43

+1

否:您也可以在mySql中使用雙重表。而在SQL Server中,如果您只需要SELECT ,則可以完全忽略它。 – davek 2010-04-18 14:04:52

0

的一種方式帶有該關鍵字的記錄:

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 

如果您想要初始重量所創建的條目的是(例如)72,用此作爲第一語句:

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

的一種方法符合舊的SQL標準,因此具有更廣泛的DBMS的兼容(截至目前的SQLite,例如,does not support MERGE)是使用a technique involving a mutex table

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

,它使的仿真INSERT IF NOT EXISTS聲明:

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; 

在OP的問題的情況下,這將是其次通過一個簡單的UPDATE:

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

其實我發佈這個爲我自己的參考,因爲我有源(xaprb.com)不容易出現在谷歌搜索。 :) – 2013-06-12 13:45:45

+2

我喜歡你的解決方案,因爲它幾乎適用於任何DBMS。但是我發現了一點點改進。你實際上不需要互斥表。只要做一個子選擇,所以'從互斥體LEFT JOIN test_table'變成'FROM(SELECT 0 AS i)'作爲互斥體LEFT JOIN test_table' – JHoffmann 2014-09-12 03:51:11

相關問題