Я думаю, есть два случая:
- Переместить одну строку, чтобы она отображалась раньше в порядке.
- Переместить одну строку, чтобы она появилась позже в порядке.
Это в любом случае нетривиально. Не ясно, существует ли уникальное ограничение на столбец «порядок»; конечный результат, безусловно, должен иметь уникальный порядок.
Обозначения:
- «Вкл» относится к строке со значением «порядок = n» в старых значениях
- «Nn» относится к строке с «order = n» в новых значениях
В примере (иллюстративный пример 1):
- O3 -> N1
- O1 -> N2
- O2 -> N3
В качестве альтернативы рассмотрим перемещение id = 2, чтобы он имел порядок = 4:
- O2 -> N4
- O3 -> N2
- O4 -> N3
Вы в основном добавляете или вычитаете одну из «других» строк, где это строки в старом порядке между старой позицией перемещенной строки и новой позицией перемещенной строки. В псевдокоде, используя $ old и $ new для определения позиций перемещенной строки до и после, и обрабатывая случай 1 ($ old> $ new):
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
WHEN order >= $new AND order < $old THEN order + 1
END CASE
WHERE order BETWEEN $new AND $old;
Соответствующий код для случая 2 ($ old <$ new): </p>
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
WHEN order > $new AND order <= $old THEN order - 1
END CASE
WHERE order BETWEEN $old AND $new;
Учитывая предложение WHERE в ОБНОВЛЕНИИ в целом, вы можете удалить второе КОГДА в СЛУЧАЕ и заменить его простым ELSE.
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
ELSE order + 1
END CASE
WHERE order BETWEEN $new AND $old;
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
ELSE order - 1
END CASE
WHERE order BETWEEN $old AND $new;
Я думаю, что хранимая процедура в порядке - выбор между двумя операторами на основе входных параметров $ old, $ new. Вы могли бы сделать что-то с разумным сочетанием выражений, таких как '($old - $new) / ABS($old - $new)
' и 'MIN($old, $new)
' и 'MAX($old, $new)
', где MIN / MAX не агрегаты, а функции компаратора для пары значений (как находится в Фортране, среди других языков программирования).
Обратите внимание, что я предполагаю, что во время выполнения одного оператора SQL ограничение уникальности (если оно есть) не применяется при изменении каждой строки - только после завершения оператора. Это необходимо, поскольку вы не можете контролировать порядок обработки строк. Я знаю о СУБД, где это может вызвать проблемы; Я знаю о других, где это не будет.
Все это можно сделать одним оператором SQL, но вы хотите, чтобы хранимая процедура сортировала параметры в операторе. Я использую IBM Informix Dynamic Server (11.50.FC6 в MacOS X 10.6.2), и это одна из СУБД, которая применяет уникальное ограничение для столбца 'order' в конце оператора. Я занимался разработкой SQL без ограничения UNIQUE; это тоже сработало, конечно. (И да, IDS позволяет вам откатывать операторы DDL, такие как CREATE TABLE и CREATE PROCEDURE. Что вы сказали? Ваша СУБД нет? Как странно!)
BEGIN WORK;
CREATE TABLE AnonymousTable
(
id INTEGER NOT NULL PRIMARY KEY,
title VARCHAR(10) NOT NULL,
order INTEGER NOT NULL UNIQUE
);
INSERT INTO AnonymousTable VALUES(1, 'test1', 1);
INSERT INTO AnonymousTable VALUES(2, 'test2', 2);
INSERT INTO AnonymousTable VALUES(3, 'test3', 3);
INSERT INTO AnonymousTable VALUES(4, 'test4', 4);
SELECT * FROM AnonymousTable ORDER BY order;
CREATE PROCEDURE move_old_to_new(old INTEGER, new INTEGER)
DEFINE v_min, v_max, v_gap, v_inc INTEGER;
IF old = new OR old IS NULL OR new IS NULL THEN
RETURN;
END IF;
LET v_min = old;
IF new < old THEN
LET v_min = new;
END IF;
LET v_max = old;
IF new > old THEN
LET v_max = new;
END IF;
LET v_gap = v_max - v_min + 1;
LET v_inc = (old - new) / (v_max - v_min);
UPDATE AnonymousTable
SET order = v_min + MOD(order - v_min + v_inc + v_gap, v_gap)
WHERE order BETWEEN v_min AND v_max;
END PROCEDURE;
EXECUTE PROCEDURE move_old_to_new(3,1);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(1,3);
SELECT * FROM AnonymousTable ORDER BY order;
INSERT INTO AnonymousTable VALUES(5, 'test5', 5);
INSERT INTO AnonymousTable VALUES(6, 'test6', 6);
INSERT INTO AnonymousTable VALUES(7, 'test7', 7);
INSERT INTO AnonymousTable VALUES(8, 'test8', 8);
EXECUTE PROCEDURE move_old_to_new(3,6);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(6,3);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(7,2);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(2,7);
SELECT * FROM AnonymousTable ORDER BY order;
ROLLBACK WORK;
Пары вызовов хранимой процедуры с обращенными числами каждый раз восстанавливали первоначальный порядок. Ясно, что я мог бы переопределить переменную v_inc
, чтобы вместо просто ± 1 это было «LET v_inc = v_inc - v_min + v_gap;
», а затем выражение MOD было бы просто «MOD(order + v_inc, v_gap)
». Я не проверял, работает ли это с отрицательными числами.
Адаптация к MySQL или другим СУБД оставлена читателю в качестве упражнения.