UPSERT是INSERT與UPDATE的結合語法,表示行存在時執行UPDATE,不存在時執行INSERT。執行UPSERT操作時必須指定完整的PRIMARY KEY的相關列信息。UPSERT語法支持帶時間戳的數據寫入和批量寫入,其中的UPSERT謂詞也可用INSERT來替代。
引擎與版本
UPSERT語法適用于寬表引擎和時序引擎。無版本限制。
與關系型數據庫寫入的區別
Lindorm的UPSERT語句寫入數據的效果與傳統關系型數據庫的寫入效果存在以下不同:
傳統關系型數據庫
如果表定義了PRIMARY KEY,那么當連續兩次寫入相同PRIMARY KEY的數據時(使用INSERT語句且不帶ON DUPLICATE KEY子句),第二次寫入會失敗。此時需要執行UPDATE語句,或在第二次執行INSERT語句時,在語句中帶上ON DUPLICATE KEY子句。
Lindorm
寬表引擎:使用UPSERT語句寫入數據時,即使連續兩次寫入的PRIMARY KEY相同,第二次寫入數據時也不會報錯,而是覆蓋UPSERT涉及的數據。實際上,Lindorm會將兩次寫入的數據保存為兩個版本,當使用SELECT語句查詢某行數據時,默認返回最新版本的數據。
時序引擎:使用UPSERT語句寫入數據時,連續兩次寫入的PRIMARY KEY相同,則第二次寫入的數據將直接覆蓋第一次寫入的數據。
使用UPSERT語句連續兩次寫入相同PRIMARY KEY數據的示例,請參見寫入相同PRIMARY KEY的數據。
語法
upsert_statement ::= { UPSERT | INSERT } [ hint_expression ]
INTO table_identifier columns_delaration
VALUES value_list ( ',' value_list)*
[ ON DUPLICATE KEY column_identifier =
value_literal | IGNORE ]
columns_delaration ::= '(' column_identifier ( ',' column_identifier)* ')'
value_list ::= '(' value_expression( ',' value_expression)* ')'
使用說明
HINT表達式(hint_expression)
僅寬表引擎支持HINT表達式。
支持在UPSERT語句中添加HINT參數_l_ts_,用于指定寫入數據的時間戳。_l_ts_參數的詳細介紹,請參見hintOption參數說明。
ON DUPLICATE KEY
僅寬表引擎支持ON DUPLICATE KEY
子句。
ON DUPLICATE KEY
子句用于檢查指定行是否已存在,類似于HBase中的checkAndPut操作,具體規則如下:
ON DUPLICATE KEY
子句后跟隨UPDATE,用于更新某一列的值。如果指定行已存在,則執行UPDATE關鍵字后的語句,更新指定列的值;如果指定行不存在,則既不報錯也不更新指定列的值。例如ON DUPLICATE KEY UPDATE c1 = 20
是指如果指定行已存在,則將c1列的值更新為20;如果指定行不存在則不更新也不報錯。ON DUPLICATE KEY
子句后跟隨IGNORE時,如果指定行已存在,則既不報錯也不寫入;如果指定行不存在,則直接將數據寫入。僅支持CONSISTENCY為
strong
的表。CONSISTENCY參數的詳細介紹,請參見表屬性(table_options)。如果您想要更改CONSISTENCY參數的值,請參見ALTER TABLE。
示例
假設示例表sensor的結構如下:
CREATE TABLE sensor (
device_id VARCHAR NOT NULL,
region VARCHAR NOT NULL,
time TIMESTAMP NOT NULL,
temperature DOUBLE,
humidity BIGINT,
PRIMARY KEY(device_id, region, time)
)WITH(VERSIONS=2);
寫入數據
UPSERT INTO sensor(device_id, region, time, temperature, humidity) VALUES('F07A1260','north-cn','2021-04-22 15:33:00',12.1,45);
結果驗證
您可以執行SELECT * FROM sensor;
查看數據是否寫入成功。
部分列寫入數據
UPSERT INTO sensor(device_id, region, time,temperature) VALUES('F07A1260','north-cn','2021-04-22 15:33:10',13.2);
結果驗證
您可以執行SELECT * FROM sensor;
查看數據是否寫入成功。
數據已存在時,忽略寫入(ON DUPLICATE KEY IGNORE)
在寬表引擎中,使用ON DUPLICATE KEY IGNORE
子句寫入數據:
如果device_id='F07A1260',region='north-cn',time='2021-04-22 15:33:10',temperature=13.2
的行存在,則不寫入數據;如果該行不存在,寫入數據。
UPSERT INTO sensor(device_id,region,time,temperature) VALUES('F07A1260','north-cn','2021-04-22 15:33:10',13.2) ON DUPLICATE KEY IGNORE;
結果驗證
您可以執行SELECT * FROM sensor;
查看數據是否寫入成功。
數據已存在時,更新寫入(ON DUPLICATE KEY UPDATE)
在寬表引擎中,使用ON DUPLICATE KEY UPDATE
子句寫入數據:
如果device_id='F07A1260',region='north-cn',time='2021-04-22 15:33:10',temperature=13.2
的行存在,更新temperature列的值為30;如果該行不存在,既不更新temperature列的值也不報錯。
UPSERT INTO sensor(device_id,region,time,temperature) VALUES('F07A1260','north-cn','2021-04-22 15:33:10',13.2) ON DUPLICATE KEY UPDATE temperature = 30;
結果驗證
您可以執行SELECT * FROM sensor;
查看數據是否寫入成功。
寫入帶有時間戳的數據
在寬表引擎,向表sensor中寫入一行數據,并設置時間戳為111232
。
UPSERT /*+ _l_ts_(111232) */ INTO sensor (device_id ,region ,time,temperature) VALUES('F07A1260','north-cn','2021-04-22 15:33:00',12.1);
結果驗證
您可以執行SELECT * FROM sensor;
查看數據是否寫入成功。
批量寫入數據
UPSERT INTO sensor (device_id ,region ,time,temperature) VALUES('F07A1260','north-cn','2021-04-22 15:33:20',10.6), ('F07A1261','south-cn','2021-04-22 15:33:00',18.1), ('F07A1261','south-cn','2021-04-22 15:33:10',19.7);
結果驗證
您可以執行SELECT * FROM sensor;
查看數據是否寫入成功。
寫入相同PRIMARY KEY的數據
在寬表引擎中,使用UPSERT語句連續兩次寫入相同PRIMARY KEY的數據,并通過HINT查詢寫入結果。
時序引擎連續兩次寫入的PRIMARY KEY相同,第二次寫入的數據將直接覆蓋第一次寫入的數據。
第一次寫入數據。
UPSERT INTO sensor(device_id ,region ,time,temperature,humidity) VALUES('F07A1260','north-cn','2021-04-22 15:33:10',13.2,45);
查詢寫入結果。
SELECT * FROM sensor WHERE device_id='F07A1260' AND region='north-cn';
返回結果:
+-----------+----------+-------------------------------+-------------+----------+ | device_id | region | time | temperature | humidity | +-----------+----------+-------------------------------+-------------+----------+ | F07A1260 | north-cn | 2021-04-22 15:33:10 +0000 UTC | 13.2 | 45 | +-----------+----------+-------------------------------+-------------+----------+
第二次寫入數據。
UPSERT INTO sensor(device_id ,region ,time,temperature,humidity) VALUES('F07A1260','north-cn','2021-04-22 15:33:10',16.7,52);
查詢第二次寫入后的結果。
SELECT * FROM sensor WHERE device_id='F07A1260' AND region='north-cn';
返回結果:
+-----------+----------+-------------------------------+-------------+----------+ | device_id | region | time | temperature | humidity | +-----------+----------+-------------------------------+-------------+----------+ | F07A1260 | north-cn | 2021-04-22 15:33:10 +0000 UTC | 16.7 | 52 | +-----------+----------+-------------------------------+-------------+----------+
通過返回結果可以看到,兩次寫入數據時,主鍵列device_id、region和time中的數據相同,最終temperature列第一次寫入的數據被第二次寫入的數據覆蓋。
通過HINT查詢所有版本的數據。
SELECT /*+ _l_versions_(2) */ device_id, region, time,temperature,humidity FROM sensor WHERE device_id='F07A1260';
返回結果:
+-----------+----------+-------------------------------+-------------+----------+ | device_id | region | time | temperature | humidity | +-----------+----------+-------------------------------+-------------+----------+ | F07A1260 | north-cn | 2021-04-22 15:33:10 +0000 UTC | 16.7 | 52 | | F07A1260 | north-cn | 2021-04-22 15:33:10 +0000 UTC | 13.2 | 45 | +-----------+----------+-------------------------------+-------------+----------+
通過返回結果可以看到,UPSERT語句實際是將兩次寫入的數據保存為了兩個版本。