让Postgres选择下一个最小可用ID

时间:2015-01-26 14:52:22

标签: database postgresql database-design auto-increment gaps-and-islands

我想让Postgres选择第一个下一个可用ID ,以便在以下情况下不会发生错误:

CREATE TABLE test(
id serial PRIMARY KEY,
name varchar
);

然后:

INSERT INTO test VALUES (2,'dd');
INSERT INTO test (name) VALUES ('aa');
INSERT INTO test (name) VALUES ('bb');

由于id是主要的,因此会产生约束错误 如何告诉Postgres插入带有下一个空闲ID的记录?

1 个答案:

答案 0 :(得分:7)

通常,永远不会否决serial列中的默认值。如果您有时需要手动提供ID值,请使用省略现有值的自定义函数替换DEFAULT列的标准nextval('sequence_name')子句serial

基于这个虚拟表:

CREATE TABLE test (test_id serial PRIMARY KEY, test text);

功能:

CREATE OR REPLACE FUNCTION f_test_test_id_seq(OUT nextfree bigint) AS
$func$
BEGIN
LOOP
   SELECT INTO nextfree  val
   FROM   nextval('test_test_id_seq'::regclass) val  -- use actual name of sequence
   WHERE  NOT EXISTS (SELECT 1 FROM test WHERE test_id = val);

   EXIT WHEN FOUND;
END LOOP; 
END
$func$  LANGUAGE plpgsql;

更改默认值:

ALTER TABLE test ALTER COLUMN test_id SET DEFAULT f_test_test_id_seq();

它不再是serial,但serial只是一个便利功能:

如果你在serial列之上构建它,SEQUENCE会被表列自动“拥有”,这可能是一件好事。

这是一个稍微快一点的变体:

表和序列名称是硬编码的。您可以轻松地参数化序列名称(如链接答案中)甚至表名称 - 并使用EXECUTE使用动态语句测试存在。会给你一个泛型函数,但调用会更贵一些。

CREATE OR REPLACE FUNCTION f_nextfree(_tbl regclass
                                     , _col text
                                     , _seq regclass
                                     , OUT nextfree bigint) AS
$func$
BEGIN
LOOP
   EXECUTE '
   SELECT val FROM nextval($1) val WHERE NOT EXISTS (
      SELECT 1 FROM ' || _tbl || ' WHERE ' || quote_ident(_col) || ' = val)'
   INTO  nextfree
   USING _seq;

   EXIT WHEN nextfree IS NOT NULL;
END LOOP; 
END
$func$  LANGUAGE plpgsql;

ALTER TABLE test2 ALTER COLUMN test2_id
SET DEFAULT f_nextfree('test2', 'test2_id', 'test2_test2_id_seq');

SQL Fiddle.