I have a table like this that represents a linked list. When comes_after
column is null
it means it's the first record in the linked list.
id | comes_after
--------+------------
"one" | null
"two" | "one"
"three" | "two"
"four" | "three"
How do I write a function using SQL or PLPGSQL to reorder the rows? The function function move_id_after (id_to_move string, after_id string)
has 2 arguments, the id_to_move
which is the id to move to a new position, and after_id
which is the id to move that row after. If after_id
is null it means to move it to the beginning of the list.
This is my attempt but it doesn't work, and it doesn't seem like the ideal way to be doing it. As shown in the example cases, I'd like to also be able to move a row to the very beginning of a list or to the very end, and handle the cases where nothing needs to change.
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 $$;
Here are examples of some cases of how the result should be.
Move a record to another position
select move_id_after("two", "three")
should become:
id | comes_after
--------+------------
"one" | null
"three" | "one"
"two" | "three"
"four" | "two"
Move a record to a position it's already in
select move_id_after("three", "two")
should have no change:
id | comes_after
--------+------------
"one" | null
"two" | "one"
"three" | "two"
"four" | "three"
Move the first record to last position
select move_id_after("one", "four")
should become:
id | comes_after
--------+------------
"two" | null
"three" | "two"
"four" | "three"
"one" | "four"
Move the last record to first position
select move_id_after("four", null)
should become:
id | comes_after
--------+------------
"four" | null
"one" | "four"
"two" | "one"
"three" | "two"