Ухх... Прежде всего тебе нужно заполнить колонку order (сделать это можно через ROWNUM, но т.к. в MySQL нет такой ф-ции можно изобрести простой счетчик записей )
Сделать это можно так:
set @rownum:=0;
update services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services order by services.position) p
SET s.position = p.rownum
WHERE s.id = p.id
Так... Это сделалали. Теперь для этой задачи нам нужно знаю текущую ($oldpos) и новую ($newpos = у тебя order походу) позиции материала.
Ну текущую я думаю как узнать объяснять не нужно )))
А теперь сравниваем.
Если новая позиция меньше текущей, то запрос будет следующим:
set @rownum:=0;
select p.rownum, s.id, s.name, s.position from services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p WHERE (s.id = p.id and s.position = p.rownum and s.position >= 2)
где rownum = это счетчик записей
т.к. нам надо впихнуть новую запись, то новая позиция у нас будет s.position = p.rownum + 1:
set @rownum:=0;
update services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p SET s.position = p.rownum + 1 WHERE (s.id = p.id and s.position = p.rownum and s.position >= 2)
Если же новая позиция больше текущей, то делаем тоже самое, только к rownum ничего отнимать и прибавлять не нужно:
set @rownum:=0;
update services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p SET s.position = p.rownum WHERE (s.id = p.id and s.position = p.rownum+1 and s.position <= 5)
Проверка:
set @rownum:=0;
select p.rownum, s.id, s.name, s.position from services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p WHERE (s.id = p.id and s.position = p.rownum+1 and s.position <= 5)
Это всё хорошо, только в php эти запросы использовать не очень удобно

Упростим:
Итак, мы знаем что:
Если новая позиция больше или равна текущей, то к счетчику ничего не прибавляем, т.е. set s.position = p.rownum + 0) [если oldpos=2, newpos=5]
Если новая позиция меньше текущей, то к позиция=счетчику+1, т.е. set s.position = p.rownum + 1) [в случае oldpos=5, newpos=2]
Также поступаем и новой записью - счеткик+1 и oldpos в таком случае будет max(s.position)
Теперь собственно сам запрос. Сначала селект
set @rownum:=0;
select p.rownum, s.id, s.name, s.position from services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p WHERE (s.id = p.id and s.position between least(2,5) and GREATEST(2,5))
Потом апдейт:
set @rownum:=0;
update services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p
set s.position = p.rownum + 1 WHERE (s.id = p.id and s.position between least(2,5) and GREATEST(2,5))
или так:
set @rownum:=0;
update services s, (SELECT @rownum:=@rownum+1 rownum, services.id FROM services WHERE services.id !=27 order by services.position) p
set s.position = p.rownum + {$pos} WHERE (s.id = p.id and s.position between least({$oldpos},{$newpos}) and GREATEST({$oldpos},{$newpos}))