2009-03-26 9 views
1

Ich bin auf der Suche nach der besten Möglichkeit, eine Reihe von "Posts" sowie Kommentare zu diesen Posts in SQL zu speichern. Stellen Sie sich ein Design vor, das einem "Wall" auf Facebook ähnlich ist, wo Benutzer Posts auf ihre Pinnwand schreiben können und andere Benutzer diese Posts kommentieren können. Ich muss in der Lage sein, alle Pinnwand Beiträge sowie die Kommentare anzuzeigen.Was ist der beste Weg, um eine Thread-Nachrichtenliste/Struktur in SQL zu speichern?

Als ich anfing erste, kam ich mit einem Tisch wie:

CREATE Table wallposts 
(
id uuid NOT NULL, 
posted timestamp NOT NULL, 
userid uuid NOT NULL, 
posterid uuid NOT NULL, 
parentid uuid NOT NULL, 
comment text NOT NULL 
) 

ID ist einzigartig, parentid wird auf Original-Beiträge und weisen auf eine ID null sein, wenn die Zeile einen Kommentar über ein ist vorhandener Beitrag Einfach und super schnell um neue Daten einzufügen. Um jedoch eine Auswahl tun, das würde mich zurück:

POST 1 
COMMENT 1 
COMMENT 2 
POST 2 
COMMENT 1 
COMMENT 2 

Unabhängig davon, welcher Reihenfolge die Reihen gab es in der Datenbank als äußerst schwierig erwiesen. Ich kann natürlich nicht einfach nach Datum sortieren, da jemand Post 1 nach Post 2 kommentieren könnte. Wenn ich LINKE VERBINDUNGEN mache, um den übergeordneten Post in allen Zeilen zu erhalten und dann nach diesem Datum zu sortieren, werden alle ursprünglichen Posts zusammen gruppiert, da sie den Wert null haben.

Dann habe ich diese Idee:

CREATE TABLE wallposts 
(
id uuid NOT NULL, 
threadposted timestamp, 
posted timestamp, 
... 
comment text 
) 

Auf einem Original-Beitrag, threadposted und geschrieben würde das gleiche sein. Bei einem Kommentar wäre der Zeitstempel der Zeitpunkt, zu dem der ursprüngliche Beitrag gepostet wurde, und der Zeitpunkt, zu dem der Kommentar zu diesem Thread gepostet wurde. Jetzt kann ich nur tun:

select * from wallposts order by threadposted, posted; 

Das funktioniert gut, aber eine Sache ärgert mich. Wenn zwei Personen gleichzeitig einen Post erstellen, werden Kommentare zu den beiden Posts zusammengefügt, da sie denselben Timestamp haben. Ich könnte "Ticks" statt einer Datetime verwenden, aber die Genauigkeit beträgt immer noch 1/1000 Sekunde. Ich könnte auch eine eindeutige Beschränkung für Threatposted und Posted einrichten, die Einfügungen ein wenig teurer macht, aber wenn ich mehrere Datenbankserver in einer Farm hatte, ist die Chance einer Kollision immer noch da. Fast hätte ich das trotzdem gemacht, da die Chancen dafür extrem gering sind, aber ich wollte sehen, ob ich meinen Kuchen essen kann und es trotzdem habe. Meistens für meine eigene Bildungswissheit.

Die dritte Lösung wäre, diese Daten in Form eines Graphen zu speichern. Jeder Knoten würde einen Zeiger v-links und v-rechts haben. Ich könnte nach "links" bestellen, was den Baum in der Reihenfolge durchqueren würde, die ich brauche. Jedes Mal, wenn jemand einen Kommentar einfügt, muss ich den gesamten Baum neu ausbalancieren. Dies würde eine Menge Zeilensperren und alle möglichen Probleme verursachen, wenn die Site sehr beschäftigt wäre. Außerdem ist es extrem und verursacht auch Replikationsprobleme. Also habe ich diese Idee schnell geworfen.

Ich dachte auch darüber nach, nur die ursprünglichen Posts zu speichern und dann die Kommentare in einer binären Form zu serialisieren, denn wer kümmert sich um einzelne Kommentare. Dies wäre sehr schnell, wenn jedoch ein Benutzer seinen Kommentar löschen oder einen neuen Kommentar an das Ende anhängen möchte, muss ich diese Daten deserialisieren, die Struktur ändern, sie dann serialisieren und die Zeile aktualisieren. Wenn mehrere Personen gleichzeitig den gleichen Beitrag kommentieren, kann es zu zufälligen Problemen kommen.

Also hier ist was ich schließlich getan habe. Ich erkundige mich nach allen Posts geordnet nach dem eingegebenen Datum. In der Middleware-Schicht durchlaufe ich das Recordset und erstelle einen "Stapel" von Original-Posts, jeder Knoten auf dem Stack zeigt auf eine verknüpfte Liste von Kommentaren. Wenn ich auf einen originalen Post stoße, schiebe ich einen neuen Knoten auf den Stapel und wenn ich auf einen Kommentar stoße, füge ich einen Knoten zur verknüpften Liste hinzu. Ich organisiere das im Speicher, damit ich das Recordset einmal durchqueren kann und O (n) habe. Nachdem ich die In-Memory-Darstellung der Wand erstellt habe, durchquere ich diese Datenstruktur erneut und schreibe HTML aus.Das funktioniert super und hat super schnelle Inserts und super schnelle Selects und keine seltsamen Zeilensperrprobleme; Allerdings ist es auf meiner Präsentationsebene etwas schwerer und erfordert, dass ich eine In-Memory-Repräsentation der Wand des Benutzers erstelle, um Dinge herum zu bewegen, so dass es in der richtigen Reihenfolge ist. Trotzdem glaube ich, dass dies der beste Ansatz ist, den ich bisher gefunden habe.

Ich dachte, ich würde mit anderen SQL-Experten überprüfen, ob es eine bessere Möglichkeit gibt, dies mit einigen seltsamen JOINS oder UNIONS oder etwas zu tun, das immer noch mit Millionen von Benutzern performant wäre.

+0

Es tut mir leid für die nicht klar zu sein. Ich brauche die Zeilen in der Reihenfolge, in der sie in der Benutzeroberfläche angezeigt werden sollten: Post eins, Kommentare zu Post eins, Post zwei, Kommentare zu Post 2 usw. Wenn ich sie neu anordnen muss, um HTML zu rendern, könnte ich auch einfach nur erstellen eine Karte im Speicher. –

Antwort

1

Ich denke, Sie sind besser dran mit einem einfacheren Modell mit einer "ParentID" auf Kommentar für die Verschachtelung von Kommentaren. Ich denke nicht, dass es in der Regel eine gute Übung ist, Datumsangaben als Schlüssel zu verwenden, besonders in diesem Fall, wo Sie nicht wirklich brauchen, und eine Identitäts-ID wird ausreichen. Hier ist ein einfaches Beispiel, das funktionieren könnte:

Post 
---- 
ID (PK) 
Timestamp 
UserID (FK) 
Text 

Comment 
------- 
ID (PK) 
Timestamp 
PostID (FK) 
ParentCommentID (FK nullable) -- allows for nested comments 
Text 
+0

Das ist im Grunde, was ich anfing, aber das Schreiben einer SELECT-Anweisung, die jeden Beitrag gefolgt von ihren Kommentaren in der Reihenfolge zurückgeben würde erwies sich als sehr schwierig. Deshalb habe ich mich von diesem Ansatz abgewandt. Vielleicht könnten Sie beschreiben, wie Sie dies mit Ihrem obigen Schema tun können? –

0

Sie sollten in "verschachtelte Sätze" schauen. Sie ermöglichen das einfache Abrufen einer Hierarchie mit einer einzigen Abfrage. Here ist ein Artikel über sie

Wenn Sie SQL Server 2008 verwenden, ist es für sie durch die ‚hierarchyID‘ Typ integrierte Unterstützung.

Einfügungen und Aktualisierungen sind teurer und komplizierter, wenn Sie nicht über die integrierte Unterstützung verfügen, aber die Abfrage ist viel schneller und einfacher.

EDIT: Verdammt, verpasste den Teil, wo Sie bereits davon wussten. (checkte von einem Handy).

+0

Ja, ich benutze PostgreSQL 8.x - Ich habe diesen Ansatz überprüft, aber es sieht so aus, als müssten Sie jedes Mal, wenn Sie etwas einfügen, eine ganze Reihe von Zeilen aktualisieren, was die Technik nicht gerade wünschenswert macht. –

+0

Ja, das ist das Hauptproblem mit diesem System.Obwohl die Neuverteilung auf den Eintrag der obersten Ebene (in Analogie zu einer Blog-Engine beschränkt, wäre sie auf einen Blogpost beschränkt) und nicht auf die gesamte Tabelle beschränkt wäre, könnte sie aus verschiedenen Gründen dennoch unerwünscht sein. –

+0

Wenn ich die linken und rechten Besuchsknoten in der Baumstruktur gespeichert habe, würde jede Einfügung in die Baumstruktur jeden Knoten danach beeinflussen. In meinem Fall, wenn Sie Post 2 kommentierte, müssten alle Kommentare zu Post 3, 4, usw. neu nummeriert werden. –

0

Möchten Sie, dass andere Kommentare kommentieren können, d. H. Hat der Baum unendliche Tiefe?

Wenn Sie nur Beiträge haben und dann kommentiert diese Stellen Sie auf dem richtigen Weg zu beginnen dann waren und ich glaube, die folgende SQL diese Anforderung erfüllen würde (Ungeprüfte Tippfehler kann so sein)

SELECT posts.id, 
     posts.posted AS posted_at, 
     posts.userid AS posted_by, 
     posts.posterid, 
     posts.comment AS post_text, 
     comments.posted AS commented_at, 
     comments.userid AS commented_by, 
     comments.comment AS comment_text 
FROM wallposts AS posts 
LEFT OUTER JOIN wallposts AS comments ON comments.parent_id = posts.id 
ORDER BY posts.posted, comments.posted 

Diese Technik, ein Self-Join, verbindet die Tabelle einfach mit sich selbst, indem Tabellenaliasnamen verwendet werden, um die Joins anzugeben.

+0

Wie bereits erwähnt, habe ich diese Technik bereits ausprobiert, aber die obige SQL wird die Zeilen nicht sinnvoll sortieren. Ich würde am Ende alle Kommentare und dann alle Beiträge erhalten, da ich durch eine verbundene Spalte bestelle und alle "Null" -Werte zusammenklumpen würden. –

0

Wenn wir bei Ihrem Tabellendesign bleiben ... Ich denke, dass Sie einen speziellen Wert in der Parent-ID-Spalte benötigen, um Original-Posts von Kommentaren zu trennen (vielleicht nur NULL, wenn Sie die Definition dieser Spalte in NULL ändern). Dann funktioniert Self-Join. Etwas wie dieses:

SELECT posts.comment as [Original Post], 
comments.comment as Comment 
FROM wallposts AS posts 
LEFT OUTER JOIN wallposts AS comments 
ON posts.id=comments.parentID 
WHERE posts.parentID IS NULL 
ORDER BY posts.posted, comments.posted 

Das Resultset zeigt Original Post vor jedem Kommentar und hat die richtige Reihenfolge.

(Dies wurde unter Verwendung von SQL Server durchgeführt, so dass ich bin mir nicht sicher, ob es in Ihrer Umgebung funktioniert.)

+0

Ihre WHERE-Klausel filtert jedoch alle ursprünglichen Posts heraus. Damit würde alles, was ich bekommen würde, die Kommentarzeilen sein. –

+0

Oh, vielleicht verstehe ich, was du sagst. Wenn ich das Dataset durchlaufe, rendere ich [Original Post] nur einmal und erkenne dann, wenn es sich ändert. Das würde definitiv funktionieren, es ist sowieso eine Option. –

Verwandte Themen