我最近由于不知道SERIAL字段会增加是否插入数据的问题而陷入困境。
我在此问题上阅读的大多数答案都讨论了如何防止在列中出现漏洞,我可以肯定的是,在大多数情况下,这与所要解决的问题无关,而且肯定也没有我的情况。
我的情况是我的软件的特定用户正在使用某种功能,该功能导致对单个记录执行数百万次upsert。该记录用作状态信息,当我幼稚的时候,我很高兴地没有意识到当INTEGER id字段nextval()达到其极限时即将发生的错误,即以下错误:
错误:整数超出范围 SQL状态:22003
所以我的问题过去是,曾经发生过,在发生冲突回滚的情况下,如何防止id字段增加下一个序列值。
我希望其他人将他们的知识添加到我的解决方案中。
答案 0 :(得分:0)
我为缓解此问题而采取的立即解决方案是将列更改为BIGINT,如下所示:
ALTER TABLE MyTable ALTER COLUMN idMyTable TYPE BIGINT;
在我的案例中,记录数非常少(<1000),因此执行起来很琐碎。
一旦这一切都解决了,就该寻找解决潜在问题的解决方案了。我的解决方案不太可能像使用SERIAL字段那样高效,因此,如果您要实现与我所做的类似的事情,请根据您的用例牢记这一点-总是在某处进行权衡。
请考虑下表和插入/查询的数据:
CREATE TABLE TestTable ( id SERIAL PRIMARY KEY NOT NULL, Key TEXT UNIQUE NOT NULL, Val TEXT );
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'banana') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'apple') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'peach') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Animal', 'horse') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
SELECT * FROM TestTable;
id Key Val
1 Fruit peach
4 Animal horse
在这种情况下,即使未在TestTable中创建任何新记录,Fruit更新期间的每个冲突也使SERIAL值增加。
现在这是我目前正在使用的解决方法。如果有人知道如何将表名连接到“ NEW.id”,我很想听听,因为我想将ID列idTablename命名为一致性。
CREATE OR REPLACE FUNCTION IncrementSerial()
RETURNS trigger AS $fn$
BEGIN
EXECUTE format('SELECT COALESCE( MAX( id ), 0 ) + 1 FROM %I.%I;',TG_TABLE_SCHEMA,TG_TABLE_NAME) INTO NEW.id;
RETURN NEW;
END
$fn$ LANGUAGE 'plpgsql'
CREATE TABLE TestTable ( id INTEGER PRIMARY KEY NOT NULL, Key TEXT UNIQUE NOT NULL, Val TEXT );
CREATE TRIGGER trgIncrementSerial
BEFORE INSERT ON TestTable
FOR EACH ROW
EXECUTE PROCEDURE IncrementSerial()
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'banana') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'apple') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'peach') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Animal', 'horse') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
SELECT * FROM TestTable;
id Key Val
1 Fruit peach
2 Animal horse
如您所见,SERIAL ID现在仅使用下一个最高ID号,这对于我的大多数用例来说都是理想的选择。
很显然,如果id密钥必须始终唯一,这将是一个问题,因为删除最后一条记录将释放该id。如果这不是问题(例如,id仅用于引用和级联的地方),那么这对您可能是一个很好的解决方案。