我有一张如下表:
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
如何发挥作用?
每个下一个免费号码可能取决于该过程中之前的所有更新。因此,从本质上讲,这需要一个程序解决方案。
最佳解决方案取决于基数以及重复和间隙的频率。根据你的样本我假设:
下面的代码在任何情况下都有效,但最适合所述假设。
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 个以上的附加数字,则需要做更多。
如果该表很大,请创建一个索引。
然后遍历所有重复项,并分配下一个空闲号码,并消耗它。
显然,该算法假设没有并发写入。