Ada dua kasus yang perlu dipertimbangkan, menurut saya:
- Pindahkan satu baris agar muncul lebih awal dalam pengurutan.
- Pindahkan satu baris sehingga muncul kemudian dalam urutan.
Ini tidak sepele. Tidak jelas apakah ada batasan unik pada kolom 'order'; hasil akhirnya pasti memiliki urutan yang unik.
Notasi:
- 'Aktif' mengacu pada baris dengan nilai 'order =n' di nilai lama
- 'Nn' mengacu pada baris dengan 'order =n' dalam nilai baru
Dalam contoh (ilustratif kasus 1):
- O3 --> N1
- O1 --> N2
- O2 --> N3
Sebagai alternatif, pertimbangkan untuk memindahkan id =2 sehingga memiliki orde =4:
- O2 --> N4
- O3 --> N2
- O4 --> N3
Anda pada dasarnya menambahkan atau mengurangi satu dari baris 'lainnya', di mana itu adalah baris dalam urutan lama antara posisi lama dari baris yang dipindahkan dan posisi baru dari baris yang dipindahkan. Dalam kode semu, menggunakan $old dan $new untuk mengidentifikasi posisi sebelum dan sesudah dari baris yang dipindahkan, dan menangani kasus 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;
Kode yang sesuai untuk kasus 2 ($old <$new) adalah:
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;
Mengingat klausa WHERE pada UPDATE secara keseluruhan, Anda mungkin dapat menghapus WHEN kedua dalam CASE dan menggantinya dengan ELSE sederhana.
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;
Saya pikir prosedur tersimpan sudah beres - memilih di antara dua pernyataan berdasarkan parameter input $old, $new. Anda mungkin dapat melakukan sesuatu dengan campuran ekspresi yang bijaksana seperti '($old - $new) / ABS($old - $new)
' dan 'MIN($old, $new)
' dan 'MAX($old, $new)
' di mana MIN/MAX bukan agregat tetapi fungsi pembanding untuk sepasang nilai (seperti yang ditemukan di Fortran, di antara bahasa pemrograman lainnya).
Perhatikan bahwa saya berasumsi bahwa ketika satu pernyataan SQL dijalankan, batasan keunikan (jika ada) tidak diterapkan karena setiap baris diubah - hanya ketika pernyataan selesai. Ini diperlukan karena Anda sebenarnya tidak dapat mengontrol urutan pemrosesan baris. Saya tahu DBMS di mana ini akan menyebabkan masalah; Saya tahu orang lain di mana itu tidak.
Itu semua dapat dilakukan dalam satu pernyataan SQL - tetapi Anda ingin prosedur tersimpan untuk memilah parameter ke pernyataan. Saya menggunakan IBM Informix Dynamic Server (11.50.FC6 pada MacOS X 10.6.2), dan itu adalah salah satu DBMS yang memberlakukan batasan unik pada kolom 'pesanan' di akhir pernyataan. Saya melakukan pengembangan SQL tanpa batasan UNIK; yang bekerja juga, tentu saja. (Dan ya, IDS memungkinkan Anda untuk memutar kembali pernyataan DDL seperti CREATE TABLE dan CREATE PROCEDURE. Apa yang Anda katakan? DBMS Anda tidak? Sungguh aneh!)
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;
Pasangan pemanggilan dari stored procedure dengan angka dibalik mengembalikan urutan aslinya setiap kali. Jelas, saya dapat mendefinisikan ulang v_inc
variabel sehingga bukannya hanya ±1, itu adalah 'LET v_inc = v_inc - v_min + v_gap;
' dan kemudian ekspresi MOD akan menjadi 'MOD(order + v_inc, v_gap)
'. Saya belum memeriksa apakah ini berfungsi dengan angka negatif.
Adaptasi ke MySQL atau DBMS lainnya dibiarkan sebagai latihan untuk pembaca.