如何有效地检查 PostgreSQL 中已使用和未使用值的序列

Ala*_*yne 2 postgresql sequence gaps-and-islands

在 PostgreSQL (9.3) 中,我有一个表定义为:

CREATE TABLE charts
( recid serial NOT NULL,
  groupid text NOT NULL,
  chart_number integer NOT NULL,
  "timestamp" timestamp without time zone NOT NULL DEFAULT now(),
  modified timestamp without time zone NOT NULL DEFAULT now(),
  donotsee boolean,
  CONSTRAINT pk_charts PRIMARY KEY (recid),
  CONSTRAINT chart_groupid UNIQUE (groupid),
  CONSTRAINT charts_ichart_key UNIQUE (chart_number)
);

CREATE TRIGGER update_modified
  BEFORE UPDATE ON charts
  FOR EACH ROW EXECUTE PROCEDURE update_modified();
Run Code Online (Sandbox Code Playgroud)

我想用如下序列替换chart_number:

CREATE SEQUENCE charts_chartnumber_seq START 16047;
Run Code Online (Sandbox Code Playgroud)

以便通过触发器或函数,添加新图表记录自动按升序生成新图表编号。但是,没有现有的海图记录可以更改海图编号,而且多年来分配的海图编号一直存在跳跃现象。因此,在为新图表记录分配新图表编号之前,我需要确保尚未使用“新”图表编号,并且没有为具有图表编号的任何图表记录分配不同的编号。

如何才能做到这一点?

Erw*_*ter 5

考虑不做。首先阅读这些相关的答案:

如果你仍然坚持填补空白,这里有一个相当有效的解决方案:

1.为了避免对该表查找下一个失踪的大部分地区chart_number,创建一个帮助目前所有的空白表一次

CREATE TABLE chart_gap AS
SELECT chart_number
FROM   generate_series(1, (SELECT max(chart_number) - 1  -- max is no gap
                           FROM charts)) chart_number
LEFT   JOIN charts c USING (chart_number)
WHERE  c.chart_number IS NULL;
Run Code Online (Sandbox Code Playgroud)

2.设置charts_chartnumber_seq为当前最大值并转换chart_number为实际serial列:

SELECT setval('charts_chartnumber_seq', max(chart_number)) FROM charts;

ALTER TABLE charts
   ALTER COLUMN chart_number SET NOT NULL
 , ALTER COLUMN chart_number SET DEFAULT nextval('charts_chartnumber_seq');

ALTER SEQUENCE charts_chartnumber_seq OWNED BY charts.chart_number; 
Run Code Online (Sandbox Code Playgroud)

细节:

3.虽然chart_gap不是空的chart_number从那里取下一个。要通过并发事务解决可能的竞争条件,而不使事务等待,请使用咨询锁:

WITH sel AS (
   SELECT chart_number, ...  -- other input values
   FROM   chart_gap
   WHERE  pg_try_advisory_xact_lock(chart_number)
   LIMIT  1
   FOR    UPDATE
   )
, ins AS (
   INSERT INTO charts (chart_number, ...) -- other target columns
   TABLE sel 
   RETURNING chart_number
   )
DELETE FROM chart_gap c
USING  ins i
WHERE  i.chart_number = c.chart_number;
Run Code Online (Sandbox Code Playgroud)

或者,Postgres 9.5或更高版本可以方便FOR UPDATE SKIP LOCKED地使这更简单和更快:

...
   SELECT chart_number, ...  -- other input values
   FROM   chart_gap
   LIMIT  1
   FOR    UPDATE SKIP LOCKED
...
Run Code Online (Sandbox Code Playgroud)

详细解释:

检查结果。填写所有行后,这将返回 0 行受影响。(您可以使用 签入 plpgsql IF NOT FOUND THEN ...)。然后切换到一个简单的INSERT

   INSERT INTO charts (...)  -- don't list chart_number
   VALUES (...);  --  don't provide chart_number
Run Code Online (Sandbox Code Playgroud)