I have a table like so:
categoryID categoryName
----------------------------
1 A
2 B
3 C
You could keep order as integer and renumber all the items between a drag's source index and destination index because they can't drag that far, especially as only 20 odd categories. Mulit-item drags make this more complicated however.
Float is easier, but each time they move you find the middle you could very quickly run out of precission, I would write a test for this to check it doesn't finally give up working if you keep moving the 3rd item to the 2nd pos over and over.
Example:
1,2,3
Move 3rd to 2nd
1,1.5,2
Move 3rd to 2nd
1,1.25,1.5
Move 3rd to 2nd
1,1.125,1.25
Do that in an excel spread sheet and you'll find the number becomes too small for floats to deal with in about 30 iterations.
If the number of changes is rather small you can generate a clumsy but rather efficient UPDATE statement if the you know the ids of the involved items:
UPDATE categories
JOIN (
SELECT 2 as categoryID, 3 as new_order
UNION ALL
SELECT 3 as categoryID, 4 as new_order
UNION ALL
SELECT 4 as categoryID, 2 as new_order) orders
USING (categoryId)
SET `order` = new_order;
or (which I like less):
UPDATE categories
SET `order` = ELT (FIND_IN_SET (categoryID, '2,3,4'),
3, 4, 2)
WHERE categoryID in (2,3,4);
UPD:
Assuming that you know the current id of the category (or its name), its old position, and its new position you can use the following query for moving a category down the list (for moving up you will have to change the between
condition and new_rank
computation to rank+1
):
SET @id:=2, @cur_rank:=2, @new_rank:=4;
UPDATE t1
JOIN (
SELECT categoryID, (rank - 1) as new_rank
FROM t1
WHERE rank between @cur_rank + 1 AND @new_rank
UNION ALL
SELECT @id as categoryID, @new_rank as new_rank
) as r
USING (categoryID)
SET rank = new_rank;
The idea with Float sounds reasanoble, just don't show these numbers to a user -)
Whenever user moves an entry up or down, you can figure out entries above and below. Just take their Order number and find mean value - that is a new order for the entry that has been moved.
Ok, here's the same that @newtover suggests, but these 2 simple queries can be much easier understood by any other developer, even unexperienced.
Let's say we have a table t1:
id name position
-------------------------------------
1 A 1
2 B 2
3 C 3
4 D 4
5 -E- 5
6 F 6
Let's move item 'E' with id=5 to 2nd position:
1) Increase positions for all items between the old position of item 'E' and the desired position of 'E' (positions 2, 3, 4)
UPDATE t1 SET position=position+1 WHERE position BETWEEN 2 AND 4
2) Now there is no item at position 2, so 'E' can take it's place
UPDATE t1 SET position=2 WHERE id=5
Results, ordered by 'position'
id name position
-------------------------------------
1 A 1
5 -E- 2
2 B 3
3 C 4
4 D 5
6 F 6
Just 2 simple queries, no subqueries.
Restriction: column 'position' cannot be UNIQUE. But perhaps with some modifications it should work as well.
Haven't tested this on large datasets.