本文介紹在AnalyticDB PostgreSQL版數據庫中,如何使用INSERT ON CONFLICT語法覆蓋寫入數據。
針對數據寫入時有主鍵沖突的情況,INSERT ON CONFLICT語法可以將沖突主鍵的INSERT行為轉換為UPDATE行為,從而實現沖突主鍵的覆蓋寫入。該特性又稱UPSERT覆蓋寫,與MySQL的REPLACE INTO類似。
注意事項
僅支持行存表,不支持列存表(由于列存表不支持唯一索引,所以該特性無法支持列存表)。
僅V6.3.6.1及以上內核版本支持在分區表中使用。如何升級內核版本,請參見版本升級。
不支持在UPDATE的SET子句中更新分布列和主鍵列。
不支持在UPDATE的WHERE子句中使用子查詢。
不支持Updatable View(可更新視圖)。
不支持在同一條INSERT語句中對同一主鍵插入多條數據(國際SQL標準約束)。
SQL語法
覆蓋寫入語法基于INSERT語句,INSERT語句的語法大綱如下:
[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ]
{ DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }
[ ON CONFLICT [ conflict_target ] conflict_action ]
[ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]
其中,conflict_target為:
( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] )
其中,conflict_action為:
DO NOTHING
DO UPDATE SET { column_name = { expression | DEFAULT } |
( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] )
} [, ...]
[ WHERE condition ]
ON CONFLICT子句可以實現覆蓋寫入。該子句由conflict_target和conflict_action組成。
參數 | 說明 |
conflict_target |
|
conflict_action | 用于指定沖突后需要執行的動作。取值說明:
|
示例
創建一個表t1,表中擁有4列,其中a列為主鍵,建表語句如下:
CREATE TABLE t1 (a int PRIMARY KEY, b int, c int, d int DEFAULT 0);
對表t1插入一行數據,主鍵列a的值為0,插入數據語句如下:
INSERT INTO t1 VALUES (0,0,0,0);
查看表數據:
SELECT * FROM t1;
返回信息如下:
a | b | c | d
---+---+---+---
0 | 0 | 0 | 0
(1 row)
如果再對表t1插入一行數據,主鍵列a的值還是0,則會返回一個報錯,插入數據語句如下:
INSERT INTO t1 VALUES (0,1,1,1);
報錯信息如下:
ERROR: duplicate key value violates unique constraint "t1_pkey"
DETAIL: Key (a)=(0) already exists.
如果不希望出現上述報錯信息,可以使用本文介紹的覆蓋寫入特性來進行處理:
使用ON CONFLICT DO NOTHING子句:主鍵沖突的情況下,不執行任何操作(適用于有沖突丟棄沖突數據的場景)。
插入數據語句如下:
INSERT INTO t1 VALUES (0,1,1,1) ON CONFLICT DO NOTHING;
查看表數據:
SELECT * FROM t1;
表t1沒有進行任何操作,返回示例如下:
a | b | c | d ---+---+---+--- 0 | 0 | 0 | 0 (1 row)
使用ON CONFLICT DO UPDATE子句:主鍵沖突的情況下,更新非主鍵的列(適用于全部列覆蓋寫入的場景)。
插入數據語句如下:
INSERT INTO t1 VALUES (0,2,2,2) ON CONFLICT (a) DO UPDATE SET (b, c, d) = (excluded.b, excluded.c, excluded.d);
或
INSERT INTO t1 VALUES (0,2,2,2) ON CONFLICT (a) DO UPDATE SET b = excluded.b, c = excluded.c, d = excluded.d;
在DO UPDATE SET子句中,可以使用excluded表示沖突的數據構成的偽表,在主鍵沖突的情況下,引用偽表中列的值覆蓋原來列的值。上述語句中,新插入的數據
(0,2,2,2)
構成了一個偽表,偽表包含1行4列數據,表名為excluded,可以使用excluded.b, excluded.c, excluded.d
去引用偽表中的列。查看表數據:
SELECT * FROM t1;
表t1中的非主鍵列進行了更新,返回示例如下:
a | b | c | d ---+---+---+--- 0 | 2 | 2 | 2 (1 row)
除了上述兩種情況,覆蓋寫入功能支持更多使用場景,場景如下:
主鍵沖突的情況下,在部分列中覆蓋寫入數據(適用于基于沖突數據覆蓋部分列的場景):
例如主鍵沖突后,僅覆蓋c列的數據,插入數據語句如下:
INSERT INTO t1 VALUES (0,0,3,0) ON CONFLICT (a) DO UPDATE SET c = excluded.c;
查看表數據:
SELECT * FROM t1;
返回示例如下:
a | b | c | d ---+---+---+--- 0 | 2 | 3 | 2 (1 row)
主鍵沖突的情況下,更新部分列的數據(適用于基于原始數據更新部分列場景):
例如主鍵沖突后,將d列的數據加1,插入數據語句如下:
INSERT INTO t1 VALUES (0,0,3,0) ON CONFLICT (a) DO UPDATE SET d = t1.d + 1;
查看表數據:
SELECT * FROM t1;
返回示例如下:
a | b | c | d ---+---+---+--- 0 | 2 | 3 | 3 (1 row)
主鍵沖突的情況下,更新數據為默認值(適用于沖突后,回退數據到默認值的場景):
例如主鍵沖突后,將d列恢復到默認值(上文中d列的默認值為0),插入數據語句如下:
INSERT INTO t1 VALUES (0,0,3,0) ON CONFLICT (a) DO UPDATE SET d = default;
查看表數據:
SELECT * FROM t1;
返回示例如下:
a | b | c | d ---+---+---+--- 0 | 2 | 3 | 0 (1 row)
插入多條數據:
例如插入2行數據,其中主鍵沖突的行不進行任何操作,主鍵不沖突的行正常插入,插入數據語句如下:
INSERT INTO t1 VALUES (0,0,0,0), (1,1,1,1) ON CONFLICT DO NOTHING;
查看表數據:
SELECT * FROM t1;
返回示例如下:
a | b | c | d ---+---+---+--- 0 | 2 | 3 | 0 1 | 1 | 1 | 1 (2 rows)
例如插入2行數據,主鍵沖突的行進行覆蓋寫入,主鍵不沖突的行正常插入,插入數據語句如下:
INSERT INTO t1 VALUES (0,0,0,0), (2,2,2,2) ON CONFLICT (a) DO UPDATE SET (b, c, d) = (excluded.b, excluded.c, excluded.d);
查看表數據:
SELECT * FROM t1;
返回示例如下:
a | b | c | d ---+---+---+--- 0 | 0 | 0 | 0 1 | 1 | 1 | 1 2 | 2 | 2 | 2 (3 rows)
插入的數據來自于子查詢,如果主鍵沖突,則覆蓋寫入(用于合并兩表數據或更復雜的INSERT INTO SELECT場景):
創建表t2,數據結構與表t1一致,建表語句如下:
CREATE TABLE t2 (like t1);
在表t2中插入兩行數據,插入數據語句如下:
INSERT INTO t2 VALUES (2,22,22,22),(3,33,33,33);
將表t2的數據插入表t1,如果主鍵沖突,則覆蓋寫入非主鍵的列,插入數據語句如下:
INSERT INTO t1 SELECT * FROM t2 ON CONFLICT (a) DO UPDATE SET (b, c, d) = (excluded.b, excluded.c, excluded.d);
查看表數據:
SELECT * FROM t1;
返回示例如下:
a | b | c | d ---+----+----+---- 0 | 0 | 0 | 0 1 | 1 | 1 | 1 2 | 22 | 22 | 22 3 | 33 | 33 | 33 (4 rows)