考慮すべき2つのケースがあると思います:
- 1行移動して、注文の早い段階で表示されるようにします。
- 1行移動して、注文の後半に表示されるようにします。
どちらにしても自明ではありません。列'order'に一意性制約があるかどうかは明確ではありません。最終結果は確かに一意の順序になるはずです。
表記:
- 「オン」は、古い値の値が「order=n」の行を指します
- 「Nn」は、新しい値の「order=n」の行を指します
例(ケース1の例):
- O3-> N1
- O1-> N2
- O2-> N3
別の方法として、id =2を移動して、順序が4になるようにすることを検討してください。
- O2-> N4
- O3-> N2
- O4-> N3
基本的に、「他の」行から1つを加算または減算します。これらは、移動された行の古い位置と移動された行の新しい位置の間の古い順序の行です。擬似コードでは、$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)に対応するコードは次のとおりです。
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;
全体としてUPDATEのWHERE句を指定すると、CASEの2番目のWHENを削除して、単純な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に基づいて2つのステートメントから選択します。 '($old - $new) / ABS($old - $new)
などの表現を適切に組み合わせて何かを行うことができる場合があります。 'および'MIN($old, $new)
'および'MAX($old, $new)
'ここで、MIN / MAXは集合体ではなく、値のペアのコンパレーター関数です(他のプログラミング言語の中でも特にFortranに見られるように)。
単一のSQLステートメントが実行されている間、各行が変更されるときに一意性制約(存在する場合)は適用されないことを前提としていることに注意してください-ステートメントが完了したときのみ。行が処理される順序を実際に制御することはできないため、これが必要です。これが問題を引き起こすDBMSを知っています。私はそれがない他の人を知っています。
すべてを単一のSQLステートメントで実行できますが、ストアドプロシージャでステートメントのパラメータを分類する必要があります。私はIBMInformixDynamic Server(MacOS X 10.6.2では11.50.FC6)を使用しています。これは、ステートメントの最後にある「order」列に一意の制約を適用するDBMSの1つです。 UNIQUE制約なしでSQLの開発を行いました。もちろん、それもうまくいきました。 (はい、IDSを使用すると、CREATETABLEやCREATEPROCEDUREなどのDDLステートメントをロールバックできます。何と言いましたか?DBMSはそうではありませんか?古風な趣があります!)
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または他のDBMSへの適応は、読者の演習として残されています。