Eigene Ordnung in der Datenbank

Dominik [Datenbanken, Tricks und Tools]

Um dies etwas genauer auszuführen werden wir als erstes eine Tabelle benötigen. Diese kann in PHPMyAdmin mit folgendem Script erstellt werden.

CREATE TABLE IF NOT EXISTS `MySequenceTbl` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `Name` varchar(255) NOT NULL,
  `Sequence` smallint(6) NOT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Sequence` (`Sequence`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

INSERT INTO MySequenceTbl (`Id`, `Name`, `Sequence`) VALUES(1, 'value 1', 1);
INSERT INTO MySequenceTbl (`Id`, `Name`, `Sequence`) VALUES(2, 'value 2', 2);
INSERT INTO MySequenceTbl (`Id`, `Name`, `Sequence`) VALUES(3, 'value 3', 3);
INSERT INTO MySequenceTbl (`Id`, `Name`, `Sequence`) VALUES(4, 'value 4', 4);

Jetzt haben wir eine Tabelle mit ein paar Testdaten die eine manuelle Sortierung haben. Um ein neues Element in die Tabelle einzufügen benötigen wir den aktuell höchsten Sequence Wert. Diesen müssen wir dann um 1 inkremementieren.

SELECT Sequence + 1 FROM MySequenceTbl ORDER BY Sequence DESC LIMIT 0, 1;

Falls noch kein Element vorhanden ist verwenden wir fix 1. Damit ist das Einfügen von neuen Werten bereits abgeschlossen, keine grosse Sache bis jetzt.

Wenn wir jetzt ein Element verschieben möchten wird es bereits etwas komplizierter. Es benötigt dazu 3 SQL Scripts welche bevorzugt in einer Transaktion oder sogar als StoreProcedure ausgeführt werden. Die Abfolge beginnt mit den Update des Elements welches verschoben werden soll. Diesem weisen wir die Sequence 0 zu um den Unique-Key in der Folge nicht zu verletzen.

UPDATE MySequenceTbl SET Sequence = 0 WHERE Id = 3;

Nun folg ein Schritt, bei welchem wir wissen müssen, ob das Element nach links oder rechts verschoben wird. Wird es nach links verschoben müssen wir alle Elemente zwischen der alten und der neuen Position nach rechts verschieben (also Sequence = Sequence + 1). Wenn wir es in die andere Richtung verschieben ist es andersrum (also Sequence = Sequence - 1). Bei diesem Vorgang ist dzu beachten, dass dias ORDER BY korrekt gesetzt wird. Sonst kann es zu Unique-Key Verletzungen kommen.

-- nach links
UPDATE MySequenceTbl SET Sequence = Sequence + 1 WHERE Sequence >= 1 AND Sequence <= 3 ORDER BY Sequence DESC;
-- oder nach rechts
UPDATE MySequenceTbl SET Sequence = Sequence - 1 WHERE Sequence >= 1 AND Sequence <= 3 ORDER BY Sequence ASC;

Am Ende können wir das zu verschiebende Element an die vorgesehene Stelle schieben, da diese jetzt frei ist.

UPDATE MySequenceTbl SET Sequence = 1 WHERE Id = 3;

Damit kommen wir zum Löschen eines Elements, was logischerweise das Löschen desjenigen voraussetzt. Danach haben wir eine Lücke in der Sequence was wir nicht wollen. Diese wird geschlossen, indem alle Elemente mit grösserer Sequence um 1 dekrementiert werden.

DELETE FROM MySequenceTbl WHERE Id = 2;
UPDATE MySequenceTbl SET Sequence = Sequence - 1 WHERE Sequence >= 2 ORDER BY Sequence ASC;

Somit haben wir alle UseCases behandelt. Wir haben, wenn alles korrekt implementiert ist, gibt es immer konsistente Sequence-Folgen innerhalb der Tabelle. Nun kommt es aber vor, dass in einer Tabelle mehrere Sequence-Folgen vorkommen können, z.B. wenn wir eine Spalte KategorieId einfügen, welche dann einen Unique-Key mit der Sequence Spalte bildet. Dies benötigt für die oberen Fälle ein paar Erweiterungen in den WHERE's und bringt ein weiterer UseCase mit sich. Das Verschieben eines Elementes von einer Kategorie in eine andere. Evtl .gibt es darüber dann zu einem späteren Zeitpunkt einen weiteren Blogeintrag.

zurück