Here和here是一些类似的问题,但不一样,我可以将它们应用到我的解决方案中。
我需要删除一行并使用单个查询重新编号文档中的行(从已删除的行开始更高)。 我得到的答案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等更复杂的事情是可能的,我认为这也是可能的。
如果有人可以开发描述的查询。
答案 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
及以上内容放入一个交易中即可。