PostgreSQL,删除具有重新编号列值的行
Posted
技术标签:
【中文标题】PostgreSQL,删除具有重新编号列值的行【英文标题】:PostgreSQL, delete a row with renumbering column values 【发布时间】:2013-11-28 11:40:26 【问题描述】:Here 和 here 是一些与此类似的问题,但不太相同,我可以将它们应用于我的解决方案。
我需要使用单个查询在文档中删除一行并重新编号行(从删除的行开始)。 我得到一个答案here,它不在单个查询中,因此不适合简单使用。
这是我的示例表,其中包含所有文档的所有行。 工作文件将是编号为 2 的“brkalk”。 例如,我们将删除第 '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 到 4 行重新编号(实际上是在“brred”列中重新编号),如果可能的话,在单个 SQL 命令中进行查询,以便我可以将其应用于所有我的文档并为此类删除创建函数。
因为像swap and renumber 这样更复杂的事情是可能的,我认为也有可能。
如果有人可以开发描述的查询,请。
【问题讨论】:
不可能在一条 SQL 语句中做你想做的事。但这可能在单个事务或存储过程中是可能的。但是,通常,这种要求表明您需要重新设计表格。 (另外,您需要对 brkalk, brred 的唯一约束吗?) 嗨,迈克,我不知道我是否说得好“单一 SQL 语句”,但我给出了我们之前解决的示例,哪些是功能性的(交换和重新编号)所以我想“单一事务”会没事。不幸的是,现在我无法重新设计表格。由于客户端程序这样做,因此不需要关注唯一约束。只是为了重新编号文档“brkalk”中的“brred”列。 【参考方案1】:如果您准备使用 PostgreSQL 扩展(如可写common table expressions)来嵌套多个语句,则可以使用单个语句执行此操作。
事实上,甚至不需要 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;
而不是期望物理顺序是一致的。如果你必须有这样的排序,你应该用物化视图来做,这样你就可以用数据生成一个新的边表,而不是就地更新原始数据。
【讨论】:
嗨,克雷格,现在我需要一些时间来应用它。 丑吗?我不会这么说的!我见过的最好的和功能齐全的查询之一。一站式解决方案。我会记住锁定。非常感谢。【参考方案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;
正如 Craig 所指出的,没有必要在单个语句中执行此操作。只需将delete
和以上内容放在一个事务中即可。
【讨论】:
嗯。除了子查询与 CTE 措辞之外,完全相同的解决方案。 @CraigRinger:当我写的时候,你的答案中没有更新 - 否则我不会有。 是的,我编辑的时候你的还没有发布。有时候就是这样。 非常感谢“no_name”。那保证,我需要一些时间在 NET 环境中应用您的解决方案,然后我会参考情况。以上是关于PostgreSQL,删除具有重新编号列值的行的主要内容,如果未能解决你的问题,请参考以下文章