2010-05-30 5 views
14

ich die Liste "hallo" darstellen wollen, "Hallo", "Auf Wiedersehen", "Guten Tag", "grüß dich" (mit dieser Reihenfolge), in einer SQL-Tabelle:Wie man in einer geordneten Liste in SQL darstellt und einfügt?

pk | i | val 
------------ 
1 | 0 | hi 
0 | 2 | hello 
2 | 3 | goodbye 
3 | 4 | good day 
5 | 6 | howdy 

'pk' ist die Primärschlüsselspalte. Ignoriere seine Werte.

'i' ist der "Index", der die Reihenfolge der Werte in der Spalte 'val' definiert. Es ist nur verwendet, um die Reihenfolge festzulegen und die Werte sind ansonsten unwichtig.

Das Problem, das ich habe, ist mit dem Einfügen von Werten in die Liste unter Beibehaltung der Reihenfolge. Zum Beispiel, wenn ich "hey" einfügen möchte und es zwischen "Hallo" und "Auf Wiedersehen" erscheinen soll, dann muss ich die 'i' Werte von "Auf Wiedersehen" und "guten Tag" verschieben (aber vorzugsweise nicht "howdy"), um Platz für den neuen Eintrag zu schaffen.

Gibt es also ein Standard-SQL-Muster für die Shift-Operation, sondern nur die Elemente, die notwendig sind? (Beachten Sie, dass eine einfache "UPDATE-Tabelle SET i = i + 1 WHERE i> = 3" nicht funktioniert, weil sie die Eindeutigkeitseinschränkung für 'i' verletzt und auch die "Howdy" -Zeile unnötig aktualisiert.)

Oder gibt es eine bessere Möglichkeit, die geordnete Liste darzustellen? Ich nehme an, Sie könnten "i" einen Fließkommawert machen und Werte dazwischen wählen, aber dann müssen Sie einen separaten Rebalancing-Vorgang haben, wenn kein solcher Wert existiert.

Oder gibt es einen Standardalgorithmus zum Generieren von Zeichenfolgenwerten zwischen beliebigen anderen Zeichenfolgen, wenn ich 'i' ein varchar machen würde?

Oder sollte ich es nur als eine verkettete Liste darstellen? Ich habe das vermieden, weil ich auch gerne eine SELECT .. ORDER BY machen könnte, um alle Elemente in Ordnung zu bringen.

+1

möglich Duplikat von [Aktualisieren Sie eine Liste von Dingen, ohne jeden Eintrag zu treffen] (http://stackoverflow.com/questions/2824428/update-a-list-of-things-without-hitting-every-entry) –

+1

BTW : Ihr Kommentar * "UPDATE-Tabelle SET i = i + 1 WHERE i> = 3" funktioniert nicht, da es die Eindeutigkeitseinschränkung für 'i' * verletzt definitiv nicht in SQL Server gilt. Die Einschränkungen werden am Ende überprüft. Welche RDBMS verwenden Sie? –

+0

Mit SQLite 3. – Travis

Antwort

1

Sie können dies leicht erreichen, indem Sie einen kaskadierenden Trigger verwenden, der jeden 'Index' -Eintrag gleich dem neuen Eintrag in der Einfüge-/Aktualisierungsoperation auf den Indexwert +1 aktualisiert. Dies wird durch alle Zeilen kaskadieren, bis die erste Lücke die Kaskade stoppt - siehe das zweite Beispiel in this blog entry für eine PostgreSQL-Implementierung.

Dieser Ansatz sollte unabhängig von dem verwendeten RDBMS funktionieren, vorausgesetzt, er bietet Unterstützung für Trigger zum Auslösen vor ein Update/einfügen. Es macht im Grunde, was Sie tun würden, wenn Sie Ihr gewünschtes Verhalten im Code implementieren (erhöhen Sie alle folgenden Indexwerte, bis Sie auf eine Lücke stoßen), aber auf eine einfachere und effektivere Weise.

Alternativ, wenn Sie mit einer Beschränkung auf SQL Server leben können, überprüfen Sie die hierarchyid type. Während Sie hauptsächlich verschachtelte Hierarchien definieren, können Sie sie auch für die flache Reihenfolge verwenden. Es ähnelt in gewisser Weise Ihrem Ansatz mit Gleitkommazahlen, da es die Einfügung zwischen zwei Positionen ermöglicht, indem Bruchwerte zugewiesen werden, wodurch die Aktualisierung anderer Einträge vermieden wird.

+0

Ich benutze SQLite, das anscheinend keine Kaskadierung/rekursiven Trigger unterstützt (obwohl es "vor" Trigger unterstützt). – Travis

+1

@Travis: Es scheint, als ob sie die Unterstützung für rekursive Trigger ab Version 3.6.18 hinzugefügt haben, aber Sie müssen sie explizit aktivieren: http://www.sqlite.org/pragma.html # pragma_recursive_triggers - Ich bin mir nicht sicher, ob die 'Kaskade' in Ihrem Fall als rekursiv gilt, da sie niemals die eingefügte/aktualisierte Zeile selbst betreffen sollte, sondern immer eine andere Zeile, so dass es sich trotzdem lohnt. –

+0

Danke, habe das nicht gesehen. Allerdings gibt es eine einkompilierte Obergrenze für die Rekursionstiefe (standardmäßig 1000), und ich weiß nicht, ob es sehr hohe Werte verarbeiten kann, was für mich ein Problem sein könnte. Was ich schließlich beschlossen habe, war, die Eindeutigkeitsbeschränkung für 'i' zu entfernen und für den Moment nur den Ansatz "i = i + 1 WHERE i> = new_i" zu verwenden. Ich werde das noch einmal untersuchen, wenn das unnötige Aktualisieren der zusätzlichen i-Werte tatsächlich zu einem Leistungsproblem wird. – Travis

5

Als ich Ihren Beitrag gelesen habe, habe ich immer daran gedacht, 'Linked List' und am Ende denke ich immer noch, das ist der Weg zu gehen.

Wenn Sie Oracle verwenden, und die verknüpfte Liste ist eine separate Tabelle (oder sogar die gleiche Tabelle mit einer selbstreferenzierenden ID - was ich vermeiden würde), dann können Sie eine CONNECT BY Abfrage und die Pseudo-Spalte LEVEL verwenden Sortierreihenfolge festlegen.

+3

BTW - das erinnert mich an die guten alten Zeiten, als Sie Ihre BASIC-Programme neu nummerieren mussten ... das wurde normalerweise als ein separater Befehl/Aufwand gemacht, wenn Sie keine Zahlen dazwischen hatten (Sie würden mit zählen 10s im Grunde am Anfang nur für den Fall ...) – Randy

+1

Solange Sie Ihre Anwendung verwenden können, um die Sortierreihenfolge herauszufinden, erhalten Sie eine einfache NextNode-Spalte, die entweder NULL ist oder auf den nächsten Eintrag zeigt, was Sie wollen. Sie werden ORDER BY nicht nutzen können, aber die Daten sind da. INSERT-Operationen sind so einfach wie das Ändern der NextNode-Spalte der Zeile, die auf die Zeile verweist, die nach dem neuen Eintrag angezeigt werden soll. – colithium

1

Wenn Sie keine Zahlen verwenden, aber Strings, können Sie eine Tabelle haben:

pk | i | val 
------------ 
1 | a0 | hi 
0 | a2 | hello 
2 | a3 | goodbye 
3 | b | good day 
5 | b1 | howdy 

Sie fügen kann A4 zwischen a3 und b, a21 zwischen a2 und a3, a1 zwischen a0 und a2 und so auf. Sie würden eine clevere Funktion benötigen, um ein i für den neuen Wert v zwischen p und n zu erzeugen, und der Index kann länger und länger werden, oder Sie brauchen von Zeit zu Zeit eine große Neuverteilung.Ein anderer Ansatz könnte sein, eine (doppelte) verkettete Liste in der Tabelle zu implementieren, in der Sie keine Indizes speichern, sondern Links zu vorherigen und nächsten, was bedeutet, dass Sie normalerweise 1 - 1 aktualisieren müssen. 2 Elemente:

pk | prev | val 
------------ 
1 | 0 | hi 
0 | 1 | hello 
2 | 0 | goodbye 
3 | 2 | good day 
5 | 3 | howdy 

hey zwischen hallo & Auf Wiedersehen:

he gets 6 pk,

pk | prev | val 
------------ 
1 | 0 | hi 
0 | 1 | hello 
6 | 0 | hi <- ins 
2 | 6 | goodbye <- upd 
3 | 2 | good day 
5 | 3 | howdy 

das vorherige Element 012 sein würde,mit pk = 0, und goodbye, das mit hello verknüpft ist, muss in Zukunft auf hey verlinken.

Aber ich weiß nicht, ob es möglich ist, einen "Order by" -Mechanismus für viele Db-Implementierungen zu finden.

+0

Das Verwenden von Strings ist erschreckend (aber ziemlich clever). Es könnte sogar schnell sein; Mit einem Index auf Ihrer i-Spalte sollten geordnete Abfragen billig sein und aufgrund der Datenlokalität Vorteile gegenüber dem Linked-List-Ansatz bieten. –

Verwandte Themen