sql >> Databasteknik >  >> RDS >> Mysql

Flerskiktskommentarsvar:Display och lagring

Det finns många sätt. Här är ett tillvägagångssätt som jag gillar (och använder regelbundet).

Databasen

Tänk på följande databasstruktur:

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)
);

din data kommer att se ut så här:

+-----+-------------------------------------+--------------------------+---------------+
| 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...

Det är ganska enkelt att välja allt på ett användbart sätt:

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

beställning efter parent_path, date_posted kommer vanligtvis att ge resultat i den ordning du behöver dem när du skapar din sida; men du vill vara säker på att du har ett index i kommentarstabellen som stöder detta ordentligt -- annars fungerar frågan, men den är verkligen, riktigt ineffektiv:

create index comments_hier_idx on comments (parent_path, date_posted);

För en given enskild kommentar är det lätt att få den kommentarens hela träd av underordnade kommentarer. Lägg bara till en where-klausul:

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

den tillagda where-satsen kommer att använda samma index som vi redan har definierat, så vi är igång.

Observera att vi inte har använt parent_id än. I själva verket är det inte strikt nödvändigt. Men jag inkluderar det eftersom det tillåter oss att definiera en traditionell främmande nyckel för att upprätthålla referensintegritet och för att implementera överlappande raderingar och uppdateringar om vi vill. Främmande nyckelbegränsningar och kaskadregler är endast tillgängliga i INNODB-tabeller:

ALTER TABLE comments ENGINE=InnoDB;

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

Hantera hierarkin

För att använda detta tillvägagångssätt måste du naturligtvis se till att du ställer in parent_path korrekt när du infogar varje kommentar. Och om du flyttar runt kommentarer (vilket visserligen skulle vara ett konstigt användningsfall), måste du se till att du manuellt uppdaterar varje parent_path för varje kommentar som är underordnad den flyttade kommentaren. ... men båda är ganska lätta att hänga med.

Om du verkligen vill bli fancy (och om din db stöder det), kan du skriva triggers för att hantera parent_path transparent -- jag lämnar det här en övning för läsaren, men den grundläggande idén är att infoga och uppdatera triggers skulle aktiveras innan ett nytt inlägg görs. de skulle gå upp i trädet (med hjälp av parent_id). främmande nyckelrelation), och bygg om värdet för parent_path i enlighet därmed.

Det är till och med möjligt att bryta parent_path ut i en separat tabell som helt hanteras av triggers i kommentarstabellen, med några få vyer eller lagrade procedurer för att implementera de olika frågorna du behöver. På så sätt isolerar du helt och hållet din mellanskiktskod från behovet av att känna till eller bry sig om mekaniken för att lagra hierarkiinformationen.

Naturligtvis krävs inget av de tjusiga sakerna på något sätt -- det är vanligtvis ganska tillräckligt att bara släppa parent_path i tabellen och skriva lite kod i din mellannivå för att säkerställa att den hanteras korrekt tillsammans med alla andra fält du måste redan klara dig.

Införande av gränser

MySQL (och vissa andra databaser) låter dig välja "sidor" med data med hjälp av LIMIT klausul:

SELECT * FROM mytable LIMIT 25 OFFSET 0;

Tyvärr, när man hanterar hierarkiska data som denna, kommer LIMIT-satsen ensam inte att ge det önskade resultatet.

-- 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;

Istället måste vi göra ett separat val på den nivå där vi vill införa gränsen, sedan kopplar vi tillbaka det tillsammans med vår "sub-tree"-fråga för att ge det slutliga önskade resultatet.

Något så här:

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;

Lägg märke till uttalandet limit 25 offset 0 , begravd i mitten av den inre välja. Detta uttalande kommer att hämta de senaste 25 "root-level" kommentarerna.

[edit:du kanske upptäcker att du måste leka med saker lite för att få möjligheten att beställa och/eller begränsa saker precis som du vill. detta kan innefatta att lägga till information inom hierarkin som är kodad i parent_path . till exempel:istället för /{id}/{id2}/{id3}/ , kan du välja att inkludera post_date som en del av parent_path:/{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/ . Detta skulle göra det mycket enkelt att få den ordning och hierarki du vill ha, på bekostnad av att du måste fylla i fältet i förväg och hantera det när data ändras]

hoppas detta hjälper. lycka till!



  1. SQL - Kombinera flera liknande frågor

  2. WordPress Editor uppdaterar inte filer:Det går inte att kommunicera tillbaka med webbplatsen för att leta efter allvarliga fel

  3. Hur SQLite Max() fungerar

  4. Hantera en PostgreSQL Commitfest