Ich versuche zu verstehen, wie man bestellte Informationen in einer relationalen Datenbank richtig speichert.
Ein Beispiel:
Angenommen, ich habe eine Wiedergabeliste, die aus Songs besteht. In meiner relationalen Datenbank habe ich eine Tabelle Playlists
mit einigen Metadaten (Name, Ersteller usw.). Ich habe auch eine Tabelle mit der Bezeichnung " Songs
the" playlist_id
sowie songspezifische Informationen (Name, Künstler, Dauer usw.).
Wenn ein neuer Song zu einer Wiedergabeliste hinzugefügt wird, wird er standardmäßig an das Ende angehängt. Bei Bestellung auf Song-ID (aufsteigend) gilt die Reihenfolge der Hinzufügung. Was aber, wenn ein Benutzer in der Lage sein sollte, Songs in der Wiedergabeliste neu zu ordnen?
Ich hatte ein paar Ideen, jede mit ihren Vor- und Nachteilen:
- Eine aufgerufene Spalte
order
, die eine Ganzzahl ist . Wenn ein Song verschoben wird, wird die Reihenfolge aller Songs zwischen der alten und der neuen Position geändert, um die Änderung widerzuspiegeln. Dies hat den Nachteil, dass jedes Mal, wenn ein Song verschoben wird, viele Abfragen durchgeführt werden müssen und der Algorithmus für das Verschieben nicht so einfach ist wie bei den anderen Optionen. - Eine Spalte genannt
order
, die eine ist dezimal (NUMERIC
). Wenn ein Song verschoben wird, wird ihm der Gleitkommawert zwischen den beiden benachbarten Zahlen zugewiesen. Nachteil: Dezimalfelder nehmen mehr Platz in Anspruch und es kann vorkommen, dass die Genauigkeit abnimmt, es sei denn, es wird darauf geachtet, dass der Bereich nach einigen Änderungen neu verteilt wird. - Eine andere Möglichkeit wäre, ein
previous
und einnext
Feld zu haben, das auf andere Songs verweist. (oder sind NULL im Fall des ersten bzw. letzten Songs in der Wiedergabeliste; im Grunde erstellen Sie eine verknüpfte Liste ). Nachteil: Abfragen wie 'finde den X. Song in der Liste' sind nicht mehr zeitlich konstant, sondern zeitlich linear.
Welches dieser Verfahren wird in der Praxis am häufigsten angewendet? Welches dieser Verfahren ist bei mittleren bis großen Datenbanken am schnellsten? Gibt es noch andere Möglichkeiten, dies zu erreichen?
EDIT: Der Einfachheit halber gehört in diesem Beispiel ein Song nur zu einer Playlist (eine Beziehung von vielen zu eins). Natürlich könnte man auch eine Junction-Tabelle verwenden, sodass die Song-Playlist eine Beziehung von vielen zu vielen ist (und eine der oben genannten Strategien auf diese Tabelle anwenden).
update songorder set order = order - 1 where order >= 12 & order <= 42; update songorder set order = 42 where id = 123;
- das sind zwei Updates - nicht dreißig. Drei, wenn Sie eine eindeutige Einschränkung für die Bestellung festlegen möchten.Queries like 'find the Xth Song in the list' are no longer constant-time
Dies gilt auch für Option 2.Antworten:
Datenbanken sind für bestimmte Dinge optimiert. Das schnelle Aktualisieren vieler Zeilen ist eine davon. Dies gilt insbesondere dann, wenn Sie die Datenbank arbeiten lassen.
Erwägen:
Und wenn Sie zum
Beat It
Ende gehen möchten, hätten Sie zwei Fragen:Und das ist es. Dies lässt sich sehr gut mit sehr großen Zahlen skalieren. Versuchen Sie, ein paar tausend Titel in eine hypothetische Wiedergabeliste in Ihrer Datenbank aufzunehmen, und sehen Sie, wie lange es dauert, einen Titel von einem Ort an einen anderen zu verschieben. Da diese sehr standardisierte Formen haben:
Sie haben zwei vorbereitete Anweisungen, die Sie sehr effizient wiederverwenden können.
Dies bietet einige bedeutende Vorteile - die Reihenfolge der Tabelle ist etwas, über das Sie nachdenken können. Das dritte Lied hat immer eine
order
3. Die einzige Möglichkeit, dies zu gewährleisten, besteht darin, aufeinanderfolgende ganze Zahlen als Reihenfolge zu verwenden. Durch die Verwendung von Pseudolisten, Dezimalzahlen oder Ganzzahlen mit Lücken können Sie diese Eigenschaft nicht garantieren. In diesen Fällen besteht die einzige Möglichkeit, den n-ten Titel zu erhalten, darin, die gesamte Tabelle zu sortieren und den n-ten Datensatz abzurufen.Und das ist viel einfacher, als Sie denken. Es ist einfach herauszufinden, was Sie tun möchten, um die beiden Aktualisierungsanweisungen zu generieren, und damit andere Personen sich diese beiden Aktualisierungsanweisungen ansehen und erkennen können, was gerade getan wird.
quelle
order
da diesorder by
ein Schlüsselwort ist?order
Wert beim Hinzufügen eines neuen Songs zu einer Wiedergabeliste zu erhalten. Angenommen, es ist der 9. Song. Gibt es eine bessere Möglichkeit, 9 in den Song einzufügen,order
als eineCOUNT
vor dem Hinzufügen der Platte?Zunächst ist aus Ihrer Beschreibung nicht ersichtlich, was Sie getan haben, aber Sie benötigen eine
PlaylistSongs
Tabelle, die aPlaylistId
und a enthält undSongId
beschreibt, welche Songs zu welchen Wiedergabelisten gehören.In dieser Tabelle müssen Sie Bestellinformationen hinzufügen.
Mein Lieblingsmechanismus ist mit reellen Zahlen. Ich habe es kürzlich implementiert und es hat wie ein Zauber funktioniert. Wenn Sie ein Lied an eine bestimmte Position verschieben möchten, berechnen Sie den neuen
Ordering
Wert als Durchschnitt derOrdering
Werte des vorherigen und des nächsten Lieds. Wenn Sie eine reelle 64-Bit-Zahl verwenden, wird Ihnen die Genauigkeit ungefähr zur gleichen Zeit ausgehen, zu der die Hölle zufriert. Wenn Sie jedoch Ihre Software für die Nachwelt schreiben, sollten Sie erwägenOrdering
, allen Titeln in jedem Titel schöne gerundete Ganzzahlenwerte zuzuweisen Wiedergabeliste hin und wieder.Als zusätzlichen Bonus ist hier der Code, den ich geschrieben habe, der dies implementiert. Natürlich können Sie es nicht so verwenden, wie es ist, und es wäre im Moment zu viel Arbeit für mich, es für Sie zu bereinigen. Deshalb poste ich es nur, damit Sie Ideen daraus erhalten.
Die Klasse ist
ParameterTemplate
(was auch immer, fragen Sie nicht!). Die Methode ruft die Liste der Parametervorlagen ab, zu denen diese Vorlage von ihrem übergeordneten Element gehörtActivityTemplate
. (Was auch immer, fragen Sie nicht!) Der Code enthält einige Schutzmaßnahmen gegen mangelnde Präzision. Der Divisor wird zum Testen verwendet: Beim Komponententest wird ein großer Divisor verwendet, um die Genauigkeit schnell zu verringern und somit den Präzisionsschutzcode auszulösen. Die zweite Methode ist öffentlich und "nur für den internen Gebrauch; nicht aufrufen", damit der Testcode sie aufrufen kann. (Es kann nicht paket-privat sein, da mein Testcode nicht im selben Paket enthalten ist wie der Code, den er testet.) Das Feld, das die Bestellung steuert, wird aufgerufenOrdering
und übergetOrdering()
und aufgerufensetOrdering()
. Sie sehen kein SQL, da ich die objektrelationale Zuordnung über den Ruhezustand verwende.quelle
Was für mich bei einer kleinen Liste in der Größenordnung von 100 Artikeln funktionierte, war ein hybrider Ansatz:
Sie erhalten also eine Ganzzahlreihenfolge ohne Lücken, die in einer Dezimalspalte gespeichert ist. Es ist ziemlich sauber, ich fühle. Wenn Sie Hunderttausende von Zeilen auf einmal aktualisieren müssen, ist die Skalierung möglicherweise nicht besonders gut. Aber wenn ja, warum verwenden Sie überhaupt eine benutzerdefinierte Sortierung? (Hinweis: Wenn Sie eine große Tabelle mit Millionen von Benutzern haben, aber jeder Benutzer nur ein paar hundert Elemente zum Sortieren hat, können Sie den oben beschriebenen Ansatz verwenden, da Sie ohnehin eine where-Klausel verwenden, um die Änderungen auf nur einen Benutzer zu beschränken )
quelle