Wie wird eine Tabelle entworfen, die neu sequenziert werden kann?

8

Ich muss eine Designentscheidung über die Datenbank treffen. Die Anforderung besteht darin, dass eine Datenbanktabelle ein Feld AUTO_INCREMENT PRIMARY KEY mit dem Namen id aufweist. Standardmäßig wird jeder Zeile der Benutzer (im Web) nach id sortiert angezeigt. Zum Beispiel, wenn in der Tabelle 4 Datensätze vorhanden sind. Die Benutzeroberfläche zeigt Zeilen in der Reihenfolge 0, 1, 2, 3 .

Nun ist es erforderlich, dass der Benutzer Drag & amp; Zeile in UI ablegen, um die Reihenfolge zu ändern. Nehmen wir an, Benutzer ziehen rom 3 und lassen es vor 0 fallen. So wird die Anzeigesequenz zu 3, 0, 1, 2 . Diese Sequenz sollte in die Datenbank persistent sein.

Ich frage mich, wie ich die Datenbanktabelle so gestalten soll, dass sie dauerhaft und skalierbar ist. Mein erster Gedanke ist, dass jede Zeile ein " Sequenz " Feld hat, das die Anzeigesequenz angibt. Standardmäßig sollte der Wert der ID id entsprechen. Wenn Sie Daten aus der Datenbank zur Anzeige auswählen, werden die Zeilen aufsteigend nach sequence anstatt nach id sortiert.

Wenn die Sequenz geändert wird, wird sie auf einen neuen Wert aktualisiert. Das Ergebnis ist, dass es viele Änderungen in anderen Zeilen enthalten kann. Nehmen wir das obige Beispiel, ursprünglich ist die Tabelle wie folgt:

%Vor%

Nach dem Ziehen der Zeile mit der ID 3 zuerst. Die Sequenz wird auf 0 aktualisiert. Gleichzeitig sollte auch die Zeile mit der ID 0, 1, 2 aktualisiert werden.

%Vor%

Ich fürchte, dieser Ansatz wird dazu führen, dass die Re-Sequenz viel kostet und nicht skalierbar ist. Ich nehme an, dass die Sequenz durch Multiplikation von id mit K (z. B. 10) initialisiert werden kann. Dadurch bleiben Lücken zwischen dem sequence -Wert für die Einfügung. Die Lücke kann jedoch immer noch aufgebraucht werden, wenn K + 1 Zeilen in diese Lücke verschoben werden.

%Vor%

Dies scheint ein häufiges Problem beim Datenbankdesign zu sein. Hat jemand eine bessere Idee, dies zu erreichen?

    
Morgan Cheng 17.10.2009, 05:09
quelle

8 Antworten

10

Die offensichtliche Antwort für mich ist, die letzte Lösung zu verwenden, die Sie erwähnt haben, aber mit Dezimalen (Gleitkommazahlen).

Also fangen Sie an, sagen Sie: {0.1, 0.2, 0.3, 0.4, 0.5} . Wenn Sie den letzten Eintrag zwischen 0.2 und 0.3 verschieben, wird 0.25 angezeigt. Wenn Sie es nach oben verschieben, wird 0.05 angezeigt. Jedes Mal nehmen Sie einfach den Mittelpunkt der beiden Zahlen auf beiden Seiten. Mit anderen Worten, der Durchschnitt der vorherigen / nächsten Elemente.

Eine andere ähnliche Lösung besteht darin, Zeichen zu verwenden und dann alphabetisch nach Zeichenfolgen zu sortieren. Beginnend mit {1, 2, 3, 4, 5} , wenn Sie die 5 zwischen 2 und 3 bewegen, würden Sie 25 verwenden. Wenn Sie eine String-Sortierung der Liste vornehmen, behalten Sie die richtige Reihenfolge bei: {1, 2, 25, 3, 4} .

Das einzige Problem, das mir bei diesen Methoden einfällt, ist, dass Sie schließlich die Grenze der Fließkomma-Genauigkeit erreichen, d. h. versuchen, eine Zahl zwischen 0.0078125 und 0.0078124 zu finden. Ein paar Wege, um das zu lösen:

  • Führen Sie jedes Mal ein Skript aus, das alle Elemente durchläuft und sie in {0.1, 0.2, 0.3, ...} neu anordnet.
  • Verwenden Sie nicht zwei Dezimalstellen, wenn Sie eine verwenden können. Zwischen 0.2 und 0.25 könnten Sie 0.23 anstelle der berechneten 0.225 .
  • verwenden
  • Verfolgen Sie die Sequenz lokal, nicht global. Wenn Sie {0.2, 0.3, 0.6} haben und nach 0.2 einfügen möchten, können Sie die zweite auf 0.4 setzen und das neue Element in 0.3 einfügen.
DisgruntledGoat 22.10.2009, 16:49
quelle
3

Die ID und die Sequenz / Sortierreihenfolge sind getrennt und sollten nicht voneinander abhängig sein.

für eine Move-Up / Move-Down-Funktion: Sie können Sequence / SortOrder Values ​​

vertauschen

oder

Für eine Drag & Drop-Funktion:

1) Legen Sie die neue Sequenz / Bestellnummer für den ausgewählten Datensatz fest.

2) Holen Sie sich die aktuelle Sequenz der ausgewählten Datensätze und aktualisieren Sie dann den ausgewählten Datensatz mit der neuen Nummer.

3) a) Wenn die neue Sequenznummer unterhalb der aktuellen Sequenznummer liegt, werden alle Sequenznummern für die Datensätze mit einer Sequenznummer erhöht. & gt; = die neue Sequenznummer (einschließlich der ausgewählten Sequenznummer)

b) Wenn die neue Sequenznummer über der aktuellen Sequenznummer liegt, dekrementieren Sie alle Sequenznummern unter der neuen und oberhalb der aktuellen Sequenznummer.

Hoffe, das macht Sinn und ich habe es richtig gemacht (unten ist die eigentliche Umsetzung).

Ich habe dies in einer einzigen SQL-Anweisung implementiert, die etwas Logik enthält, nicht für die Puristen, aber es funktioniert gut.

Hier ist ein Beispiel (OP: Sie werden die GUID IDs in INTs ändern wollen):

%Vor%     
Mark Redman 17.10.2009 10:01
quelle
2

Wie wäre es mit verknüpften Listen? : -)

%Vor%

Eigentlich habe ich kein PostgreSQL zur Hand, um zu testen, ob das tatsächlich funktioniert (und MySQL unterstützt nicht die WITH RECURSIVE von SQL-99), und ich kann es auch nicht empfehlen.

    
ephemient 17.10.2009 05:36
quelle
1

Ich habe hier eine ähnliche Frage beantwortet: Visuelle Bestellung großer Datensätze

Wenn Sie viele Objekte verschieben, müssen Sie jedes Element loopen und verschieben und auch auf Überlauf prüfen. Alles in allem besteht die grundlegende Logik darin, eine Sortierspalte mit Lücken zu haben, die periodisch neu initialisiert werden können.

    
sindre j 22.10.2009 16:53
quelle
0

Ich habe dies getan, indem ich eine CSV-Zeichenkette der ids (db keys) in der vom Benutzer gewählten Reihenfolge an den Server zurückgegeben habe. Ich habe eine Funktion auf meiner db, die eine CSV-Zeichenfolge in eine Tabelle mit 2 Feldern konvertiert - die ID und eine Sequenz (die eigentlich ein Int mit einer Identität ist). Die Sequenzfeldwerte in dieser temporären Tabelle spiegeln die Reihenfolge der Elemente in der CSV-Zeichenfolge wider. Ich aktualisiere dann die Datentabelle mit dem neuen Sequenzfeldwert, passend zu den IDs.

Edit: Die Kopfgeldpunkte sahen so lecker aus, ich dachte, ich würde einige Details zu meiner Antwort geben. Hier ist der Code:

%Vor%

Ich habe @id_array als Variable zum Testen eingerichtet - normalerweise würde Ihre Benutzeroberfläche die ID-Werte in ihrer überarbeiteten Reihenfolge erhalten und sie als Parameter an einen gespeicherten Prozess übergeben. Die Funktion get_id_table_from_list parst eine CSV-Zeichenfolge in eine Tabelle mit zwei 'int' Spalten: [id] und [sequence]. Die Spalte [Sequenz] ist eine Identität. Die Ergebnisse dieser Funktion, die an meinen Testdaten arbeiten, sehen folgendermaßen aus:

%Vor%

Du wirst eine Funktion brauchen, die den csv analysiert (ich kann meins posten, wenn du interessiert bist, und ich habe andere hier und da gepostet). Beachten Sie, dass in meinem Code davon ausgegangen wird, dass Sie sql server verwenden - die Sequenz hängt von einem Identitätsfeld ab und die Aktualisierungsabfrage verwendet eine T-SQL-Erweiterung (die 'from' -Klausel) - wenn Sie eine andere db verwenden, müssten Sie dies tun mache ein paar einfache Änderungen.

    
Ray 17.10.2009 19:07
quelle
0

Dieser Fix ist einfacher als Sie denken. Eine temporäre Tabelle und eine Update-Abfrage und Ihr erledigt.

%Vor%     
quelle
0

Ich weiß, dass dies eine 3 Jahre alte Frage ist, aber die Kommentare haben mir geholfen, ein ähnliches Problem zu lösen, und ich wollte meinen Code bereitstellen, falls er anderen hilft, nach etwas ähnlichem Ausschau zu halten.

Basierend auf dem Codebeispiel von @DisgruntledGoat (worüber ich übrigens danke!) habe ich den folgenden Code erstellt, der speziell für Entity Framework 4.1 Code-First geschrieben wurde. Es benötigt im Wesentlichen drei Parameter: ein Repository-Objekt, die ID eines Entitätsobjekts und einen booleschen Wert, um anzugeben, ob der DisplayOrder der Entity in der Anzeige nach oben oder unten verschoben werden soll. Die Entität muss von Entität erben, was bedeutet, dass sie einen Id-Wert haben muss und IDERderedEntity implementieren muss, was bedeutet, dass sie eine DisplayOrder float-Eigenschaft haben muss.

Die MoveDisplayOrder-Methode sucht die angrenzenden zwei Einträge (abhängig von der Verschiebungsrichtung entweder kleiner oder größer als die aktuelle Anzeigereihenfolge) und mittelt dann ihre Werte (daher ist der Float-Wert und kein ganzzahliger Wert erforderlich). Es wird dann das Repository für diese Entität aktualisieren. Wenn zu Reinigungszwecken die Anzahl der Dezimalstellen in der resultierenden neuen Anzeigereihenfolge 5 übersteigt, aktualisiert EF alle Werte in der Datenbank und setzt sie auf Werte von 1, 2, 3 usw. zurück / p>

Dieser Code funktioniert perfekt für mich und würde jedes Feedback begrüßen, wenn es aufgeräumt oder umstrukturiert werden muss.

%Vor%     
bigmac 08.01.2012 17:19
quelle
-1

- Edit: Nur für andere, die diesen Beitrag lesen, wurde ich aus irgendeinem seltsamen persönlichen Groll herausgelöscht, nicht von genereller Ungenauigkeit in Bezug auf dieses Thema, gebe diese Rücksicht beim Lesen und stimme ab, wie du es wünschst! :)

- Alt:

Der typische Weg dazu ist Ihre 'Sequence' Nummer (ich nenne sie 'SortOrder').

Es ist wirklich ziemlich einfach.

"Skalierbar" kommt nicht dazu; Da Sie bereits eine Liste aller Knoten haben, an denen Sie gerade arbeiten (Drag & Drop, wie Sie sagen), tauschen Sie diese Zahlen einfach aus.

Trivial.

    
Noon Silk 17.10.2009 05:42
quelle

Tags und Links