Question

I have a table of records in mySql. I need to maintain an order for them as specified by the user. So I've added a 'position' column.

What would be the SQL statement to update all the records when I move a specific record? I've got something like:

UPDATE items SET position = '2' WHERE id ='4';
UPDATE items SET position = position+1 WHERE position >= '2' AND id != '4';

But the greater than is going to be a less than if the record has moved down. What's the trick? Thanks!

Was it helpful?

Solution

Doing this sort of thing for e.g. sales orders with line numbers maintained by the user, I've found it best to handle it in an array in the BL or UI. Usually they will want to adjust several records, and sometimes want to say "forget it". So the easiest might be to just wait until they hit the "OK" button (or your equivalent) and then write them all back with current ordering.

You may end up dealing with deletions, too, which is another problem you can handle the same way.

OTHER TIPS

Would something like this do it?

UPDATE items 
SET position = CASE position 
  WHEN $oldpos THEN $newpos 
  ELSE position + SIGN($oldpos-$newpos)
 END
WHERE position BETWEEN LEAST( $newpos, $oldpos ) 
                AND GREATEST( $newpos, $oldpos );

I tested it a couple of times and it seems to work.

This is my take on this: my row_index column has number with a step of 10 (eg.: 0, 10, 20, ecc.). When a user updates one of the row you need to know if it's going up or down. If you have to move the 2nd row to the 4th (going down, desired row_index = 30), you set the row_index to 35; otherwise (if going up) set it to 25. Set this value:

UPDATE your_table SET row_index = 35 WHERE your_id = 1234

Now you have the correct order, but the row_indexes are messed up. To fix this, execute this two queries:

SET @pos:=-10;
UPDATE your_table SET row_index = @pos:=@pos+10 WHERE ...

This is going to update all your rows (or the one you select with the where statement) and set the row_index column without gaps (0, 10, 20, 30, ecc.). Surely this way adds load on the write operations, but I think it's the fastest way to get the order during read operations. If you delete a row you can just re run the fixing order queries.

I would suggest, at a minimum, using large increments (say 10000) between items. Then just doing an order by. If you need to move 11000 to between 9000 and 10000 then just set it to 9500 and done.

This doesn't eliminate the problem of reordering but it greatly reduces it. At some point you're better off deleting all the rows then readding them with the correct order. Doing updates like you're doing is just too error prone imho.

You might consider using a linked list type of approach, with a key to the next, and maybe the previous item in the list, then you only have to update a few records, instead of all of them.

Interesting! Okay, so there is no easy answer. I thought I was just missing something. I'd thought about maintaining an external array. That sounds like the best idea.

Maybe something like this?

$item_id = $_GET['item_id']; 
$position = $_GET['new_position']; 
$old_position = $_GET['old_position'];

if($new_position < $old_position) {
    SQL: UPDATE items SET position = position+1 WHERE position >= $new_position AND position < $old_position  
} else {  
SQL: UPDATE items SET position = position-1 WHERE position <= $new_position AND position > $old_position
}  

SQL: UPDATE items SET position = $new_position WHERE item_id = $item_id 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top