Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Mehrstufige Kommentarantworten:Anzeige und Speicherung

Es gibt viele Wege. Hier ist ein Ansatz, den ich mag (und regelmäßig verwende).

Die Datenbank

Betrachten Sie die folgende Datenbankstruktur:

CREATE TABLE comments (
  id int(11) unsigned NOT NULL auto_increment,
  parent_id int(11) unsigned default NULL,
  parent_path varchar(255) NOT NULL,

  comment_text varchar(255) NOT NULL,
  date_posted datetime NOT NULL,  

  PRIMARY KEY  (id)
);

Ihre Daten werden wie folgt aussehen:

+-----+-------------------------------------+--------------------------+---------------+
| id  | parent_id | parent_path             | comment_text             | date_posted   |
+-----+-------------------------------------+--------------------------+---------------+
|   1 | null      | /                       | I'm first                | 1288464193    | 
|   2 | 1         | /1/                     | 1st Reply to I'm First   | 1288464463    | 
|   3 | null      | /                       | Well I'm next            | 1288464331    | 
|   4 | null      | /                       | Oh yeah, well I'm 3rd    | 1288464361    | 
|   5 | 3         | /3/                     | reply to I'm next        | 1288464566    | 
|   6 | 2         | /1/2/                   | this is a 2nd level reply| 1288464193    | 

... and so on...

Es ist ziemlich einfach, alles auf brauchbare Weise auszuwählen:

select id, parent_path, parent_id, comment_text, date_posted
from comments 
order by parent_path, date_posted;

Sortieren nach parent_path, date_posted liefert normalerweise Ergebnisse in der Reihenfolge, in der Sie sie benötigen, wenn Sie Ihre Seite erstellen; aber Sie sollten sicher sein, dass Sie einen Index für die Kommentartabelle haben, der dies richtig unterstützt -- andernfalls funktioniert die Abfrage, aber sie ist wirklich, wirklich ineffizient:

create index comments_hier_idx on comments (parent_path, date_posted);

Für jeden einzelnen Kommentar ist es einfach, den gesamten Baum der untergeordneten Kommentare dieses Kommentars abzurufen. Fügen Sie einfach eine where-Klausel hinzu:

select id, parent_path, parent_id, comment_text, date_posted
from comments 
where parent_path like '/1/%'
order by parent_path, date_posted;

Die hinzugefügte where-Klausel verwendet denselben Index, den wir bereits definiert haben, also können wir loslegen.

Beachten Sie, dass wir die parent_id nicht verwendet haben noch. Tatsächlich ist es nicht unbedingt notwendig. Aber ich schließe es ein, weil es uns erlaubt, einen traditionellen Fremdschlüssel zu definieren, um die referenzielle Integrität zu erzwingen und kaskadierende Löschungen und Aktualisierungen zu implementieren, wenn wir wollen. Fremdschlüsseleinschränkungen und Kaskadierungsregeln sind nur in INNODB-Tabellen verfügbar:

ALTER TABLE comments ENGINE=InnoDB;

ALTER TABLE comments 
  ADD FOREIGN KEY ( parent_id ) REFERENCES comments 
    ON DELETE CASCADE 
    ON UPDATE CASCADE;

Verwalten der Hierarchie

Um diesen Ansatz zu verwenden, müssen Sie natürlich sicherstellen, dass Sie den parent_path festlegen richtig, wenn Sie jeden Kommentar einfügen. Und wenn Sie Kommentare verschieben (was zugegebenermaßen ein seltsamer Anwendungsfall wäre), müssen Sie sicherstellen, dass Sie jeden parent_path jedes Kommentars, der dem verschobenen Kommentar untergeordnet ist, manuell aktualisieren. ... aber das sind beide ziemlich einfache Dinge, mit denen man Schritt halten kann.

Wenn Sie wirklich ausgefallen sein möchten (und wenn Ihre Datenbank dies unterstützt), können Sie Trigger schreiben, um den parent_path transparent zu verwalten -- ich überlasse dies als Übung für den Leser, aber die Grundidee ist, dass Insert- und Update-Trigger ausgelöst werden bevor eine neue Einfügung festgeschrieben wird. sie würden den Baum hochgehen (unter Verwendung der parent_id Fremdschlüsselbeziehung) und erstellen Sie den Wert von parent_path neu entsprechend.

Es ist sogar möglich, den parent_path zu brechen in eine separate Tabelle, die vollständig von Triggern in der Kommentartabelle verwaltet wird, mit einigen Ansichten oder gespeicherten Prozeduren, um die verschiedenen Abfragen zu implementieren, die Sie benötigen. Dadurch wird Ihr Code der mittleren Ebene vollständig von der Notwendigkeit isoliert, die Mechanismen zum Speichern der Hierarchieinformationen zu kennen oder sich darum zu kümmern.

Natürlich ist keines der ausgefallenen Dinge erforderlich – es reicht normalerweise völlig aus, parent_path einfach in die Tabelle zu ziehen und etwas Code in Ihre Middle-Tier zu schreiben, um sicherzustellen, dass es zusammen mit allen anderen Feldern ordnungsgemäß verwaltet wird das musst du schon managen.

Grenzen auferlegen

MySQL (und einige andere Datenbanken) erlaubt es Ihnen, "Seiten" von Daten auszuwählen, indem Sie das LIMIT verwenden Klausel:

SELECT * FROM mytable LIMIT 25 OFFSET 0;

Beim Umgang mit hierarchischen Daten wie dieser führt die LIMIT-Klausel allein leider nicht zu den gewünschten Ergebnissen.

-- the following will NOT work as intended

select id, parent_path, parent_id, comment_text, date_posted
from comments 
order by parent_path, date_posted
LIMIT 25 OFFSET 0;

Stattdessen müssen wir auf der Ebene, auf der wir das Limit festlegen möchten, eine separate Auswahl treffen, die wir dann wieder mit unserer "Unterbaum" -Abfrage verbinden, um die endgültigen gewünschten Ergebnisse zu erhalten.

Etwa so:

select 
  a.*
from 
  comments a join 
  (select id, parent_path 
    from comments 
    where parent_id is null
  order by parent_path, post_date DESC 
  limit 25 offset 0) roots
  on a.parent_path like concat(roots.parent_path,roots.id,'/%') or a.id=roots.id)
order by a.parent_path , post_date DESC;

Beachten Sie die Anweisung limit 25 offset 0 , begraben in der Mitte der inneren Auswahl. Diese Anweisung ruft die letzten 25 "Root-Level"-Kommentare ab.

[Bearbeiten:Sie werden vielleicht feststellen, dass Sie ein bisschen mit Sachen spielen müssen, um die Möglichkeit zu bekommen, Dinge genau so zu ordnen und/oder einzuschränken, wie Sie möchten. dies kann das Hinzufügen von Informationen innerhalb der Hierarchie umfassen, die in parent_path codiert sind . zum Beispiel:statt /{id}/{id2}/{id3}/ , können Sie das post_date als Teil des parent_path angeben:/{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/ . Dies würde es sehr einfach machen, die gewünschte Reihenfolge und Hierarchie zu erhalten, auf Kosten der Notwendigkeit, das Feld im Voraus auszufüllen und es zu verwalten, wenn sich die Daten ändern]

Ich hoffe, das hilft. Viel Glück!