Reputation: 160
I have the simplified version of the table I have below. Each row has an item_order value partitioned by its parent_id.
item_id | item_name | parent_id | item_order |
---|---|---|---|
523 | fish | 1 | 1 |
562 | worm | 1 | 2 |
612 | mice | 1 | 3 |
251 | cheese | 1 | 4 |
723 | ketchup | 2 | 1 |
912 | pasta | 2 | 2 |
52 | chips | 2 | 3 |
Let's say that I want to set the 'item_order' value of 'mice' to 1.
UPDATE
my_table
SET
item_order = 1
WHERE item_id = 612;
I need the table to be updated as below. (Expected changes are highlighted.)
item_id | item_name | parent_id | item_order |
---|---|---|---|
612 | mice | 1 | 3 → 1 |
523 | fish | 1 | 1 → 2 |
562 | worm | 1 | 2 → 3 |
251 | cheese | 1 | 4 |
723 | ketchup | 2 | 1 |
912 | pasta | 2 | 2 |
52 | chips | 2 | 3 |
How do I update the item_order value of the all affected rows within the same parent_id?
Upvotes: 3
Views: 1268
Reputation: 160
I ended up solving this by using decimals for item_order column.
If you try to set the item_order of 612 to 1, decrement its new item_order value by a dummy float.
UPDATE
my_table
SET
item_order = 1 - 0.1
WHERE item_id = 612;
Now the table looks like this:
item_id | item_name | parent_id | item_order |
---|---|---|---|
612 | mice | 1 | 0.9 |
523 | fish | 1 | 1 |
562 | worm | 1 | 2 |
251 | cheese | 1 | 4 |
723 | ketchup | 2 | 1 |
912 | pasta | 2 | 2 |
52 | chips | 2 | 3 |
Then go over the rows with the same parent_id and set their item_order to the value of row_number()
function.
UPDATE
my_table
SET
item_order = T2.row_number
FROM
(SELECT item_id, item_order, row_number()
OVER (ORDER BY item_order)
FROM my_table
WHERE parent_id = (SELECT parent_id FROM my_table WHERE item_id = 612))
AS T2
WHERE T2.item_id = my_table.item_id;
Here is the result:
item_id | item_name | parent_id | item_order |
---|---|---|---|
612 | mice | 1 | 1 |
523 | fish | 1 | 2 |
562 | worm | 1 | 3 |
251 | cheese | 1 | 4 |
723 | ketchup | 2 | 1 |
912 | pasta | 2 | 2 |
52 | chips | 2 | 3 |
This example above only inserts before the given value. The dummy float would have to be added to item_order for inserting after a given value. Another issue is that all rows within the same parent id are updated regardless of whether they need an update or not.
Upvotes: 0
Reputation: 29647
You can do this with an update that gets the item_order from the item_id you want to move.
Only those orders below that changed order also need to move.
UPDATE my_table t SET item_order = case when t.item_order = t2.item_order then 1 when t.item_order < t2.item_order then t.item_order + 1 else t.item_order end FROM my_table t2 WHERE t.parent_id = t2.parent_id AND t.item_order <= t2.item_order AND t2.item_id = 612;
3 rows affected
select * from my_table order by parent_id, item_order
item_id | item_name | parent_id | item_order |
---|---|---|---|
612 | mice | 1 | 1 |
523 | fish | 1 | 2 |
562 | worm | 1 | 3 |
251 | cheese | 1 | 4 |
723 | ketchup | 2 | 1 |
912 | pasta | 2 | 2 |
52 | chips | 2 | 3 |
Demo on db<>fiddle here
Upvotes: 3
Reputation: 94859
You want to move an item in the sorted list. For this to happen you must change the item's sort key to the new position and increase all existing sort keys from that position on by one.
set @item_id = 612;
set @item_order = 1;
UPDATE my_table
SET item_order =
case when item_id = @item_id then @item_order
when item_order >= @item_order then item_order + 1
else item_order
end
WHERE parent_id = (SELECT parent_id FROM my_table WHERE item_id = @item_id);
Upvotes: 1
Reputation: 6638
Find the parent_id
record with item_id = 1
, and then update all records whose parent_id
equals parent_id
.
UPDATE my_table
SET
item_order = 1
WHERE parent_id = (SELECT parent_id FROM my_table WHERE item_id = 1);
Upvotes: 1