Mysql
 sql >> база данни >  >> RDS >> Mysql

Многостепенни отговори на коментари:Дисплей и съхранение

Има много начини. Ето един подход, който харесвам (и използвам редовно).

База данни

Помислете за следната структура на базата данни:

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

вашите данни ще изглеждат така:

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

Доста лесно е да изберете всичко по подходящ начин:

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

подреждане по parent_path, date_posted обикновено дава резултати в реда, в който ще се нуждаете от тях, когато генерирате страницата си; но ще искате да сте сигурни, че имате индекс в таблицата с коментари, който ще поддържа правилно това - в противен случай заявката работи, но е наистина, наистина неефективна:

create index comments_hier_idx on comments (parent_path, date_posted);

За всеки отделен коментар е лесно да се получи цялото дърво от подчинени коментари на този коментар. Просто добавете клауза where:

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

добавената клауза where ще използва същия индекс, който вече дефинирахме, така че сме готови.

Забележете, че не сме използвали parent_id още. Всъщност не е строго необходимо. Но го включвам, защото ни позволява да дефинираме традиционен външен ключ, за да наложим референтната цялост и да приложим каскадни изтривания и актуализации, ако искаме. Ограниченията на външния ключ и каскадните правила са налични само в INNODB таблици:

ALTER TABLE comments ENGINE=InnoDB;

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

Управление на йерархията

За да използвате този подход, разбира се, ще трябва да сте сигурни, че сте задали parent_path правилно, когато вмъквате всеки коментар. И ако премествате коментари наоколо (което определено би било странен случай на употреба), ще трябва да се уверите, че ръчно актуализирате всеки parent_path на всеки коментар, който е подчинен на преместения коментар. ... но и двете неща са доста лесни за поддържане.

Ако наистина искате да станете фантастични (и ако вашата база данни го поддържа), можете да напишете тригери за прозрачно управление на parent_path - ще оставя това упражнение за читателя, но основната идея е, че тригерите за вмъкване и актуализиране ще се задействат преди да бъде завършено ново вмъкване. те ще се изкачат по дървото (използвайки parent_id връзка с външен ключ) и изградете отново стойността на parent_path съответно.

Възможно е дори да разбиете parent_path в отделна таблица, която се управлява изцяло от тригери в таблицата с коментари, с няколко изгледа или съхранени процедури за изпълнение на различните заявки, от които се нуждаете. По този начин напълно изолирате вашия код от средно ниво от необходимостта да знаете или да се грижите за механиката на съхраняване на информацията за йерархията.

Разбира се, нито едно от фантастичните неща не се изисква по никакъв начин - обикновено е достатъчно просто да пуснете parent_path в таблицата и да напишете някакъв код във вашето средно ниво, за да гарантирате, че се управлява правилно заедно с всички останали полета вече трябва да управляваш.

Налагане на ограничения

MySQL (и някои други бази данни) ви позволява да избирате "страници" с данни с помощта на LIMIT клауза:

SELECT * FROM mytable LIMIT 25 OFFSET 0;

За съжаление, когато се работи с йерархични данни като тази, клаузата LIMIT сама по себе си няма да даде желаните резултати.

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

Вместо това трябва да направим отделен избор на нивото, на което искаме да наложим ограничението, след което да го присъединим обратно заедно с нашата заявка за "поддърво", за да дадем крайните желани резултати.

Нещо като това:

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;

Обърнете внимание на израза limit 25 offset 0 , заровен в средата на вътрешния избран. Това изявление ще извлече последните 25 коментара на „основно ниво“.

[редактиране:може да откриете, че трябва да играете малко с неща, за да получите възможността да подреждате и/или ограничавате нещата точно както ви харесва. това може да включва добавяне на информация в йерархията, която е кодирана в parent_path . например:вместо /{id}/{id2}/{id3}/ , може да решите да включите post_date като част от parent_path:/{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/ . Това би улеснило получаването на реда и йерархията, които искате, за сметка на необходимостта да попълвате полето предварително и да го управлявате, когато данните се променят]

Надявам се това да помогне. Успех!



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. fullCalendar метод за публикуване на събития към MySQL

  2. #1139 - Получих грешка „операнд за повторение невалиден“ от регулярния израз

  3. SELECT INTO Променлива в MySQL DECLARE причинява синтактична грешка?

  4. Възможно ли е кръстосано индексиране?

  5. Алтернатива на mysql_real_escape_string без свързване към DB