Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Trả lời nhận xét nhiều tầng:Hiển thị và Lưu trữ

Có rất nhiều cách. Đây là một cách tiếp cận mà tôi thích (và sử dụng thường xuyên).

Cơ sở dữ liệu

Xem xét cấu trúc cơ sở dữ liệu sau:

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

dữ liệu của bạn sẽ trông như thế này:

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

Khá dễ dàng để chọn mọi thứ theo cách có thể sử dụng được:

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

đặt hàng theo parent_path, date_posted thường sẽ tạo ra kết quả theo thứ tự bạn sẽ cần khi tạo trang của mình; nhưng bạn sẽ muốn chắc chắn rằng bạn có một chỉ mục trên bảng nhận xét sẽ hỗ trợ đúng cách này - nếu không thì truy vấn hoạt động, nhưng nó thực sự, thực sự không hiệu quả:

create index comments_hier_idx on comments (parent_path, date_posted);

Đối với bất kỳ nhận xét đơn lẻ nào đã cho, thật dễ dàng nhận được toàn bộ cây nhận xét trẻ em của nhận xét đó. Chỉ cần thêm mệnh đề where:

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

mệnh đề where được thêm vào sẽ sử dụng cùng một chỉ mục mà chúng ta đã xác định, vì vậy chúng ta nên bắt đầu.

Lưu ý rằng chúng tôi chưa sử dụng parent_id chưa. Trên thực tế, nó không hoàn toàn cần thiết. Nhưng tôi bao gồm nó vì nó cho phép chúng ta xác định một khóa ngoại truyền thống để thực thi tính toàn vẹn của tham chiếu và thực hiện xóa và cập nhật theo tầng nếu chúng ta muốn. Ràng buộc khóa ngoại và quy tắc xếp tầng chỉ có sẵn trong bảng INNODB:

ALTER TABLE comments ENGINE=InnoDB;

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

Quản lý hệ thống phân cấp

Tất nhiên, để sử dụng phương pháp này, bạn sẽ phải đảm bảo rằng bạn đặt parent_path đúng cách khi bạn chèn mỗi nhận xét. Và nếu bạn di chuyển các nhận xét xung quanh (phải thừa nhận là một usecase kỳ lạ), bạn sẽ phải đảm bảo rằng bạn cập nhật thủ công từng đường dẫn_của mỗi nhận xét phụ với nhận xét đã di chuyển. ... nhưng đó đều là những thứ khá dễ dàng để theo kịp.

Nếu bạn thực sự muốn trở nên ưa thích (và nếu db của bạn hỗ trợ nó), bạn có thể viết các trình kích hoạt để quản lý đường dẫn parent_path một cách minh bạch - Tôi sẽ để lại bài tập này cho người đọc, nhưng ý tưởng cơ bản là trình kích hoạt chèn và cập nhật sẽ kích hoạt trước khi một phụ trang mới được cam kết. họ sẽ đi bộ lên cây (sử dụng parent_id mối quan hệ khóa ngoại) và xây dựng lại giá trị của parent_path theo đó.

Thậm chí có thể phá vỡ parent_path ra một bảng riêng biệt được quản lý hoàn toàn bởi các trình kích hoạt trên bảng nhận xét, với một vài dạng xem hoặc các thủ tục được lưu trữ để triển khai các truy vấn khác nhau mà bạn cần. Do đó, hoàn toàn tách biệt mã cấp giữa của bạn khỏi nhu cầu biết hoặc quan tâm đến cơ chế lưu trữ thông tin phân cấp.

Tất nhiên, không có bất kỳ thứ gì ưa thích là bắt buộc - thường là khá đủ để chỉ cần thả đường dẫn_đường_trục vào bảng và viết một số mã trong lớp giữa của bạn để đảm bảo rằng nó được quản lý đúng cách cùng với tất cả các trường khác bạn đã phải quản lý.

Áp đặt giới hạn

MySQL (và một số cơ sở dữ liệu khác) cho phép bạn chọn "trang" dữ liệu bằng cách sử dụng LIMIT mệnh đề:

SELECT * FROM mytable LIMIT 25 OFFSET 0;

Thật không may, khi xử lý dữ liệu phân cấp như thế này, chỉ riêng mệnh đề LIMIT sẽ không mang lại kết quả mong muốn.

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

Thay vào đó, chúng ta cần phải có một lựa chọn riêng biệt ở cấp mà chúng ta muốn áp đặt giới hạn, sau đó chúng ta kết hợp trở lại đó cùng với truy vấn "cây con" của chúng ta để đưa ra kết quả mong muốn cuối cùng.

Một cái gì đó như thế này:

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ưu ý câu lệnh limit 25 offset 0 , được chôn ở giữa vùng chọn bên trong. Câu lệnh này sẽ truy xuất 25 nhận xét "cấp cơ sở" gần đây nhất.

[sửa:bạn có thể thấy rằng bạn phải chơi với mọi thứ một chút để có khả năng sắp xếp và / hoặc giới hạn mọi thứ theo cách bạn muốn. điều này có thể bao gồm việc thêm thông tin trong hệ thống phân cấp được mã hóa trong parent_path . ví dụ:thay vì /{id}/{id2}/{id3}/ , bạn có thể quyết định bao gồm post_date như một phần của đường dẫn gốc:/{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/ . Điều này sẽ giúp bạn rất dễ dàng có được thứ tự và thứ bậc mà bạn muốn, với chi phí là phải điền trước trường và quản lý nó khi dữ liệu thay đổi]

hy vọng điều này sẽ hữu ích. chúc may mắn!



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thay thế cho mysql_real_escape_string mà không cần kết nối với DB

  2. Kết nối Android với MS SQL SERVER 2008

  3. MYSQL Chuyển đổi dấu thời gian thành Tháng

  4. PHP và MySQL:Thứ tự theo ngày gần đây nhất và giới hạn 10

  5. Không có câu lệnh chuẩn bị từ phía máy chủ bằng MySQL Connector / J