如何使 postgres 函数重新排序表示链表的行?
How to make a postgres function reorder rows representing a linked list?
我有一个像这样的 table 表示链表。当 comes_after
列为 null
时,表示它是链表中的第一条记录。
id | comes_after
--------+------------
"one" | null
"two" | "one"
"three" | "two"
"four" | "three"
如何使用 SQL 或 PLPGSQL 编写函数来重新排序行?函数 function move_id_after (id_to_move string, after_id string)
有 2 个参数,id_to_move
是移动到新位置的 id,after_id
是移动该行之后的 id。如果 after_id
为空,则表示将其移动到列表的开头。
这是我的尝试,但没有用,而且似乎不是理想的实现方式。如示例案例所示,我还希望能够将一行移动到列表的开头或结尾,并处理无需更改的情况。
create function move_id_after (id_to_move string, after_id string) language plpgsql as $$
declare
AFTER_id_to_move string;
AFTER_after_id string;
id_to_move_used_to_follow string;
begin
select id from mytable where comes_after = id_to_move into AFTER_id_to_move;
select id from mytable where comes_after = after_id into AFTER_after_id;
update mytable set comes_after = id_to_move where id = AFTER_after_id;
update mytable set comes_after = AFTER_after_id where id = id_to_move returning id into id_to_move_used_to_follow;
update mytable set comes_after = id_to_move_used_to_follow where id = id_to_move_after;
end $$;
以下是一些示例,说明结果应该如何。
将记录移动到另一个位置
select move_id_after("two", "three")
应该变成:
id | comes_after
--------+------------
"one" | null
"three" | "one"
"two" | "three"
"four" | "two"
将记录移动到它已经在的位置
select move_id_after("three", "two")
应该没有变化:
id | comes_after
--------+------------
"one" | null
"two" | "one"
"three" | "two"
"four" | "three"
将第一条记录移到最后一个位置
select move_id_after("one", "four")
应该变成:
id | comes_after
--------+------------
"two" | null
"three" | "two"
"four" | "three"
"one" | "four"
将最后一条记录移到第一个位置
select move_id_after("four", null)
应该变成:
id | comes_after
--------+------------
"four" | null
"one" | "four"
"two" | "one"
"three" | "two"
如果要指定顺序,则必须使用ORDER BY 子句。任何其他解决方案都不应该起作用。对于更大的数据,您的设计不实用。对于您的设计,您每次都必须计算一些订单值,并且该计算应该基于递归调用 - 这对于图形数据库来说是好的设计,而不是关系。
关系 (table) 不是矩阵,没有开始,也没有结束。例如,当你想搜索最后一条记录时,你必须使用递归 CTE
-- searching last record in list
with recursive x as (select 0 l, id
from mytable
where comes_after is null
union all
select l + 1, mytable.id
from x join mytable on x.id = mytable.comes_after)
select id
from x
order by l desc
limit 1;
我不知道你的目标是什么,但是关系数据库不是这个工具。
这可能是有趣的学校任务,但在现实生活中可能很糟糕。它违背了关系数据库的原则。
更常用的解决方案是使用可用于 ORDER BY 子句的特殊数字列。有的喜欢
CREATE SEQUENCE test_o START WITH 1;
CREATE TABLE test(id SERIAL, v varchar, o numeric DEFAULT nextval('test_o'));
-- insert at end
INSERT INTO test(v) VALUES('ahoj');
INSERT INTO test(v) VALUES('nazdar');
INSERT INTO test(v) VALUES('bazar');
-- sort data by o
SELECT * FROM test ORDER BY o;
INSERT INTO test(v,
SELECT * FROM test ORDER BY o;
┌────┬────────┬───┐
│ id │ v │ o │
╞════╪════════╪═══╡
│ 1 │ ahoj │ 1 │
│ 2 │ nazdar │ 2 │
│ 3 │ bazar │ 3 │
└────┴────────┴───┘
在id=2
之后插入:
INSERT INTO test(v, o)
SELECT 'HELLO',
(SELECT (o + lead(o,1) OVER (ORDER BY o))/2
FROM test
WHERE o >= (SELECT o
FROM test
WHERE id = 2)
ORDER BY o
LIMIT 1);
postgres=# SELECT * FROM test ORDER BY o;
┌────┬──────────┬────────────────────┐
│ id │ v │ o │
╞════╪══════════╪════════════════════╡
│ 1 │ ahoj │ 1 │
│ 2 │ nazdar │ 2 │
│ 6 │ HELLO │ 2.5000000000000000 │
│ 3 │ bazar │ 3 │
└────┴──────────┴────────────────────┘
(4 rows)
我有一个像这样的 table 表示链表。当 comes_after
列为 null
时,表示它是链表中的第一条记录。
id | comes_after
--------+------------
"one" | null
"two" | "one"
"three" | "two"
"four" | "three"
如何使用 SQL 或 PLPGSQL 编写函数来重新排序行?函数 function move_id_after (id_to_move string, after_id string)
有 2 个参数,id_to_move
是移动到新位置的 id,after_id
是移动该行之后的 id。如果 after_id
为空,则表示将其移动到列表的开头。
这是我的尝试,但没有用,而且似乎不是理想的实现方式。如示例案例所示,我还希望能够将一行移动到列表的开头或结尾,并处理无需更改的情况。
create function move_id_after (id_to_move string, after_id string) language plpgsql as $$
declare
AFTER_id_to_move string;
AFTER_after_id string;
id_to_move_used_to_follow string;
begin
select id from mytable where comes_after = id_to_move into AFTER_id_to_move;
select id from mytable where comes_after = after_id into AFTER_after_id;
update mytable set comes_after = id_to_move where id = AFTER_after_id;
update mytable set comes_after = AFTER_after_id where id = id_to_move returning id into id_to_move_used_to_follow;
update mytable set comes_after = id_to_move_used_to_follow where id = id_to_move_after;
end $$;
以下是一些示例,说明结果应该如何。
将记录移动到另一个位置
select move_id_after("two", "three")
应该变成:
id | comes_after
--------+------------
"one" | null
"three" | "one"
"two" | "three"
"four" | "two"
将记录移动到它已经在的位置
select move_id_after("three", "two")
应该没有变化:
id | comes_after
--------+------------
"one" | null
"two" | "one"
"three" | "two"
"four" | "three"
将第一条记录移到最后一个位置
select move_id_after("one", "four")
应该变成:
id | comes_after
--------+------------
"two" | null
"three" | "two"
"four" | "three"
"one" | "four"
将最后一条记录移到第一个位置
select move_id_after("four", null)
应该变成:
id | comes_after
--------+------------
"four" | null
"one" | "four"
"two" | "one"
"three" | "two"
如果要指定顺序,则必须使用ORDER BY 子句。任何其他解决方案都不应该起作用。对于更大的数据,您的设计不实用。对于您的设计,您每次都必须计算一些订单值,并且该计算应该基于递归调用 - 这对于图形数据库来说是好的设计,而不是关系。
关系 (table) 不是矩阵,没有开始,也没有结束。例如,当你想搜索最后一条记录时,你必须使用递归 CTE
-- searching last record in list
with recursive x as (select 0 l, id
from mytable
where comes_after is null
union all
select l + 1, mytable.id
from x join mytable on x.id = mytable.comes_after)
select id
from x
order by l desc
limit 1;
我不知道你的目标是什么,但是关系数据库不是这个工具。
这可能是有趣的学校任务,但在现实生活中可能很糟糕。它违背了关系数据库的原则。
更常用的解决方案是使用可用于 ORDER BY 子句的特殊数字列。有的喜欢
CREATE SEQUENCE test_o START WITH 1;
CREATE TABLE test(id SERIAL, v varchar, o numeric DEFAULT nextval('test_o'));
-- insert at end
INSERT INTO test(v) VALUES('ahoj');
INSERT INTO test(v) VALUES('nazdar');
INSERT INTO test(v) VALUES('bazar');
-- sort data by o
SELECT * FROM test ORDER BY o;
INSERT INTO test(v,
SELECT * FROM test ORDER BY o;
┌────┬────────┬───┐
│ id │ v │ o │
╞════╪════════╪═══╡
│ 1 │ ahoj │ 1 │
│ 2 │ nazdar │ 2 │
│ 3 │ bazar │ 3 │
└────┴────────┴───┘
在id=2
之后插入:
INSERT INTO test(v, o)
SELECT 'HELLO',
(SELECT (o + lead(o,1) OVER (ORDER BY o))/2
FROM test
WHERE o >= (SELECT o
FROM test
WHERE id = 2)
ORDER BY o
LIMIT 1);
postgres=# SELECT * FROM test ORDER BY o;
┌────┬──────────┬────────────────────┐
│ id │ v │ o │
╞════╪══════════╪════════════════════╡
│ 1 │ ahoj │ 1 │
│ 2 │ nazdar │ 2 │
│ 6 │ HELLO │ 2.5000000000000000 │
│ 3 │ bazar │ 3 │
└────┴──────────┴────────────────────┘
(4 rows)