PostgreSQL,删除一行重新编号列值

时间:2013-11-28 11:40:26

标签: postgresql

Herehere是一些类似的问题,但不一样,我可以将它们应用到我的解决方案中。

我需要删除一行并使用单个查询重新编号文档中的行(从已删除的行开始更高)。 我得到的答案here不是单一查询,因此不适合简单使用。

这是我的示例表,其中包含所有文档的所有行 工作文件将是'brkalk',编号为2 例如,我们将删除行“brred”3。

DROP TABLE IF EXISTS kalksad1;

CREATE TABLE kalksad1(
kalk_id     int PRIMARY KEY,
brkalk      integer, 
brred       integer, 
description text);

INSERT INTO kalksad1 VALUES
(12, 2, 5, 'text index 12 doc 2 row 5'),
(26, 2, 1, 'text index 26 doc 2 row 1'),
(30, 2, 2, 'text index 30 doc 2 row 2'),
(32, 4, 1, 'text index 32 doc 4 row 1'),
(36, 1, 1, 'text index 36 doc 1 row 1'),
(37, 1, 2, 'text index 37 doc 1 row 2'),
(38, 5, 1, 'text index 38 doc 5 row 1'),
(39, 5, 2, 'text index 39 doc 5 row 2'),
(42, 2, 3, 'text index 42 doc 2 row 3'),
(43, 2, 4, 'text index 43 doc 2 row 4'),
(46, 3, 1, 'text index 46 doc 3 row 1'),
(47, 3, 2, 'text index 47 doc 3 row 2');

当然我可以使用SQL轻松删除一行:

DELETE FROM kalksad1 WHERE brkalk=2 AND brred=3

但是为了我的程序的良好功能,需要在一个文档中的行编号中不存在“漏洞”。

在删除之后,我在“brred”列中有行,编号为1,2,4,5。

我想获取查询,它会在删除第3行后立即重新编号第4行到第3行和第5行(实际上在“brred”列中重新编号值),如果可能,在单个SQL命令中,这样我就可以将它应用于所有我的文件和制作此类删除功能。

由于swap and renumber等更复杂的事情是可能的,我认为这也是可能的。

如果有人可以开发描述的查询。

2 个答案:

答案 0 :(得分:4)

如果您准备使用像可写common table expressions这样的PostgreSQL扩展来嵌套多个语句,则可以使用单个语句执行此操作。

事实上,甚至不需要CTE。 http://sqlfiddle.com/#!15/6f9c0/11。虽然你可以把所有的东西塞进一个(可怕的丑陋)陈述中,例如:

WITH deleted AS (
    DELETE FROM kalksad1 WHERE brkalk=2 AND brred=3
    RETURNING kalk_id, brkalk
)
UPDATE kalksad1
SET brred = k.brred_new
FROM
(
  SELECT
      k2.kalk_id, 
      row_number() OVER (PARTITION BY k2.brkalk ORDER BY k2.brred) AS brred_new
  FROM (
    SELECT k3.kalk_id, k3.brkalk, k3.brred
    FROM kalksad1 k3
    INNER JOIN deleted ON (k3.brkalk = deleted.brkalk)
    FOR UPDATE
  ) k2
) k
WHERE kalksad1.kalk_id = k.kalk_id
RETURNING kalksad1.kalk_id, kalksad1.brkalk, kalksad1.brred;

这样做非常低效,看看the query plan会告诉你。适当的索引会有所帮助,但它仍然会涉及多个表扫描。

在一个语句中完成所有操作也不会解决所有并发问题,尽管FOR UPDATE子查询应该阻止并发DELETE。在运行语句之前LOCK TABLE kalksad1 IN EXCLUSIVE MODE最安全,以防止并发插入/更新/删除。

通常认为需要此设计的设计相当破碎。您通常应该只依赖于表中的排序,并根据需要生成带编号的序列,如:

SELECT x, row_number() OVER (ORDER BY x) FROM my_table;

而不是期望物理排序是一致的。如果您必须具有这样的排序,您应该使用物化视图进行排序,这样您就可以使用数据生成新的边表,而不是就地更新原始数据。

答案 1 :(得分:2)

如果您真的需要重新编号这些项目,可以使用以下内容:

with renumber as (
  select kalk_id
         brred, 
         row_number() over (partitioin by brkalk order by brred) as rn
  from kalksad1
)
update kalksad1
  set brred = r.rn
from renumber r
where kalksad1.kalk_id = r.kalk_id;

正如克雷格指出的那样,没有必要在一个声明中这样做。只需将delete及以上内容放入一个交易中即可。