让 Postgres 选择下一个最小的可用 id
Make Postgres choose the next minimal available id
我想让 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 的记录?
一般来说,最好永远不要否决serial
列中的默认值。如果您有时 需要 手动提供 id 值,请将 serial
列的标准 DEFAULT
子句 nextval('sequence_name')
替换为省略现有值的自定义函数.
基于这个虚拟 table:
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
只是一个方便的功能:
- Safely and cleanly rename tables that use serial primary key columns in Postgres?
如果您在 serial
列之上构建它,SEQUENCE
会自动被 table 列 "owned",这可能是一件好事。
这是一个稍快的变体:
- Autoincrement, but omit existing values in the column
Table 和序列名称在这里被硬编码。您 可以 轻松参数化序列名称(就像在链接的答案中一样)甚至 table 名称 - 并使用 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() 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');
我想让 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 的记录?
一般来说,最好永远不要否决serial
列中的默认值。如果您有时 需要 手动提供 id 值,请将 serial
列的标准 DEFAULT
子句 nextval('sequence_name')
替换为省略现有值的自定义函数.
基于这个虚拟 table:
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
只是一个方便的功能:
- Safely and cleanly rename tables that use serial primary key columns in Postgres?
如果您在 serial
列之上构建它,SEQUENCE
会自动被 table 列 "owned",这可能是一件好事。
这是一个稍快的变体:
- Autoincrement, but omit existing values in the column
Table 和序列名称在这里被硬编码。您 可以 轻松参数化序列名称(就像在链接的答案中一样)甚至 table 名称 - 并使用 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() 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');