Tôi nghĩ có hai trường hợp cần xem xét:
- Di chuyển một hàng để nó xuất hiện sớm hơn trong thứ tự.
- Di chuyển một hàng để nó xuất hiện sau trong thứ tự.
Nó không phải là tầm thường theo cả hai cách. Không rõ liệu có một ràng buộc duy nhất nào đối với cột 'order' hay không; kết quả cuối cùng chắc chắn phải có một thứ tự duy nhất.
Kí hiệu:
- 'Bật' đề cập đến hàng có giá trị 'order =n' trong các giá trị cũ
- 'Nn' đề cập đến hàng có 'order =n' trong các giá trị mới
Trong ví dụ (minh họa cho trường hợp 1):
- O3 -> N1
- O1 -> N2
- O2 -> N3
Thay vào đó, hãy xem xét việc di chuyển id =2 để nó có thứ tự =4:
- O2 -> N4
- O3 -> N2
- O4 -> N3
Về cơ bản, bạn đang thêm hoặc bớt một từ các hàng 'khác', trong đó đó là các hàng theo thứ tự cũ giữa vị trí cũ của hàng đã di chuyển và vị trí mới của hàng đã di chuyển. Trong mã giả, sử dụng $ old và $ new để xác định vị trí trước và sau của hàng đã di chuyển và xử lý trường hợp 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;
Mã tương ứng cho trường hợp 2 ($ old <$ new) là:
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;
Với toàn bộ mệnh đề WHERE trên UPDATE, bạn có thể loại bỏ WHEN thứ hai trong CASE và thay thế nó bằng một ELSE đơn giản.
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;
Tôi nghĩ rằng một thủ tục được lưu trữ là theo thứ tự - lựa chọn giữa hai câu lệnh dựa trên các tham số đầu vào $ old, $ new. Bạn có thể làm điều gì đó với sự kết hợp hợp lý của các biểu thức chẳng hạn như '($old - $new) / ABS($old - $new)
'và' MIN($old, $new)
'và' MAX($old, $new)
'trong đó MIN / MAX không phải là tổng hợp mà là hàm so sánh cho một cặp giá trị (như được tìm thấy trong Fortran, trong số các ngôn ngữ lập trình khác).
Lưu ý rằng tôi đang giả định rằng trong khi một câu lệnh SQL duy nhất đang thực thi, ràng buộc về tính duy nhất (nếu có) không được thực thi khi mỗi hàng được thay đổi - chỉ khi câu lệnh hoàn thành. Điều này là cần thiết vì bạn thực sự không thể kiểm soát thứ tự các hàng được xử lý. Tôi biết về DBMS nơi điều này sẽ gây ra rắc rối; Tôi biết về những người khác ở nơi nó sẽ không.
Tất cả có thể được thực hiện trong một câu lệnh SQL duy nhất - nhưng bạn muốn một thủ tục được lưu trữ để sắp xếp các tham số cho câu lệnh. Tôi sử dụng Máy chủ động Informix của IBM (11.50.FC6 trên MacOS X 10.6.2) và đó là một trong những DBMS thực thi ràng buộc duy nhất trên cột 'đơn hàng' ở cuối câu lệnh. Tôi đã phát triển SQL mà không có ràng buộc DUY NHẤT; điều đó cũng hoạt động, tất nhiên. (Và có, IDS cho phép bạn khôi phục các câu lệnh DDL như TẠO BẢNG và TẠO THỦ TỤC. Bạn đã nói gì? DBMS của bạn không? Thật kỳ lạ!)
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;
Các cặp lệnh gọi của thủ tục được lưu trữ với các số được đảo ngược sẽ khôi phục lại thứ tự ban đầu mỗi lần. Rõ ràng, tôi có thể xác định lại v_inc
để thay vì chỉ ± 1, nó là 'LET v_inc = v_inc - v_min + v_gap;
'và khi đó biểu thức MOD sẽ chỉ là' MOD(order + v_inc, v_gap)
'. Tôi chưa kiểm tra xem điều này có hoạt động với số âm hay không.
Việc thích ứng với MySQL hoặc DBMS khác được coi là một bài tập cho người đọc.