将重复值更新为下一个空闲号码

问题描述 投票:0回答:1

我有一张如下表:

CREATE TABLE "table" (
    "index" serial PRIMARY KEY,
    "number" integer
);

这些行:

INSERT INTO "table" ("number") VALUES
(1), (2), (2), (3), (4), (4), (4), (5), (13), (13), (17);

我想将重复值更新为下一个空闲整数(第一个除外)。
对于这种情况,它应该是这样的:

(1), (2), (3), (4), (5), (6), (7), (8), (13), (14), (17)

UPDATE
如何发挥作用?

sql postgresql gaps-and-islands
1个回答
0
投票

每个下一个免费号码可能取决于该过程中之前的所有更新。因此,从本质上讲,这需要一个程序解决方案。

最佳解决方案取决于基数以及重复和间隙的频率。根据你的样本我假设:

  • 几个骗子
  • 间隙较多

下面的代码在任何情况下都有效,但最适合所述假设。

DO
$do$
DECLARE
   _id int;
   _number int;
BEGIN
   CREATE TEMP TABLE free ON COMMIT DROP AS
   SELECT number
   FROM  (SELECT generate_series(min(number), max(number) + 10) FROM tbl) n(number)
   LEFT  JOIN tbl t USING (number)
   WHERE t.number IS NULL;

   -- (only) if table is big, add an index
   CREATE INDEX ON pg_temp.free (number);
   
   FOR _id, _number IN
      SELECT id, number
      FROM (
         SELECT *, lag(number) OVER (ORDER BY number) AS last_num
         FROM   tbl
         ) dup
      WHERE dup.last_num = dup.number
   LOOP
      WITH del AS (
         DELETE FROM pg_temp.free f
         USING (
            SELECT f1.number
            FROM   pg_temp.free f1
            WHERE  f1.number > _number
            ORDER  BY f1.number 
            LIMIT  1
            ) d
         WHERE f.number = d.number
         RETURNING f.number
         )
      UPDATE tbl t
      SET    number = d.number
      FROM   del d
      WHERE  t.id = _id;
   END LOOP;
END
$do$;

小提琴

此 PL/pgSQL 代码块首先在给定表

free
的范围内创建一个空闲数字临时表 (
tbl
)。我(任意)在最高的数字之后再添加 10 个数字。如果除了最高的数字之外,您可能还需要 10 个以上的附加数字,则需要做更多。

如果该表很大,请创建一个索引。

然后遍历所有重复项,并分配下一个空闲号码,并消耗它。

显然,该算法假设没有并发写入。

© www.soinside.com 2019 - 2024. All rights reserved.