PG只支持以下几种约束延迟触发(并不是不触发,而是再事务结束的时候触发)
UNIQUE、PRIMARY KEY、REFERENCES(外键)和 EXCLUDE
测试例子:
sql
CREATE TABLE logs (
id INTEGER,
log_time TIMESTAMP,
message TEXT,
CONSTRAINT pk_logs
PRIMARY KEY (id)
DEFERRABLE
);
BEGIN;
SET CONSTRAINTS pk_logs DEFERRED;
-- 可以暂时插入重复ID,最后再修复
INSERT INTO logs (id, log_time, message) VALUES
(1, '2024-01-01 10:00:00', 'Message 1'),
(1, '2024-01-01 10:01:00', 'Message 2'), -- 暂时允许重复
(2, '2024-01-01 10:02:00', 'Message 3');
-- 修复重复的ID
UPDATE logs SET id = 3 WHERE id = 1 AND message = 'Message 2';
COMMIT; -- 提交时才检查主键唯一性
建表的时候指定DEFERRABLE是可以延迟的意思,是延迟的前提条件,默认是 INITIALLY IMMEDIATE
这时候需要在开始事务之后使用
SET CONSTRAINTS DEFERRED语句来确定是否要真的延迟
当然也可以指定INITIALLY DEFERRED 这样在事务中就不需要执行
SET CONSTRAINTS DEFERRED 语句了
如下图所测试
指定INITIALLY DEFERRED
sql
CREATE TABLE logs (
id INTEGER,
log_time TIMESTAMP,
message TEXT,
CONSTRAINT pk_logs
PRIMARY KEY (id)
DEFERRABLE INITIALLY DEFERRED
);
begin;
INSERT INTO logs (id, log_time, message) VALUES
(1, '2024-01-01 10:00:00', 'Message 1'),
(1, '2024-01-01 10:01:00', 'Message 2'),
(2, '2024-01-01 10:02:00', 'Message 3');
-- 修复重复的ID
UPDATE logs SET id = 3 WHERE id = 1 AND message = 'Message 2';
COMMIT; -- 提交时才检查主键唯一性
修改约束
当然也可以修改约束延迟模式
但是需要先删除掉约束,重建的时候指定
sql
postgres=# alter table logs drop constraint pk_logs;
ALTER TABLE
postgres=# alter table logs add constraint pk_logs primary key(id) deferrable initially deferred;
ALTER TABLE
当然oracle和达梦默认就是延迟模式
如下所示:
sql
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.18.0.0.0
SQL> create table tab(id numeric primary key);
Table created.
SQL> insert into tab values (1);
1 row created.
SQL> insert into tab values(2);
1 row created.
SQL> commit;
Commit complete.
SQL> update tab set id=id+1;
2 rows updated.
SQL> commit;
Commit complete.
SQL>
达梦
sql
SQL> create table tab(id int);
executed successfully
used time: 4.234(ms). Execute id is 1702.
SQL> alter table tab add constraint tab_pkey primary key(id);
executed successfully
used time: 12.044(ms). Execute id is 1703.
SQL> insert into tab values (1),(2);
affect rows 2
used time: 1.255(ms). Execute id is 1704.
SQL> commit;
executed successfully
used time: 1.605(ms). Execute id is 1705.
SQL> select * from v$version;
LINEID BANNER
---------- ---------------------------------
1 DM Database Server 64 V8
2 DB Version: 0x7000c
3 03134284194-20240703-234060-20108
4 Msg Version: 12
5 Gsu level(5) cnt: 0
used time: 0.336(ms). Execute id is 1706.
SQL> update tab set id=id+1;
affect rows 2
used time: 1.135(ms). Execute id is 1707.
SQL> commit;
executed successfully
used time: 1.596(ms). Execute id is 1708.
SQL>