所以我正在尝试创建一个评论系统,您可以在其中回复已经回复的评论(允许您创建理论上无限的回复帖子).我希望它们按时间顺序显示(最新的),但当然回复应该直接在原始评论下面.如果有多条评论回复同一条评论,则回复也应按时间顺序排列(仍在原评论的下方).我还想限制评论组的数量(一组评论只有一个评论,而不是一个回复),比方说,25.我应该如何设置MySQL表,以及我将使用哪种查询提取我想要的东西?
这是我的数据库的简化版本:
IDint(11)NOT NULL AUTO_INCREMENT,
DatePosteddatetime NOT NULL,
InReplyToint(11)NOT NULL DEFAULT'0',
对不起,如果这有点令人困惑,我不知道如何以不同的方式说出来.我脑子里已经有这个问题了几个月了,每当我解决一个问题时,我最终会得到另一个......
有很多方法.这是我喜欢的一种方法(并定期使用).
数据库
考虑以下数据库结构:
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)
);
Run Code Online (Sandbox Code Playgroud)
您的数据将如下所示:
+-----+-------------------------------------+--------------------------+---------------+
| 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...
Run Code Online (Sandbox Code Playgroud)
以可用的方式选择所有内容相当容易:
select id, parent_path, parent_id, comment_text, date_posted
from comments
order by parent_path, date_posted;
Run Code Online (Sandbox Code Playgroud)
parent_path, date_postedorder by 通常会在您生成页面时按照您需要的顺序生成结果; 但是你要确保你在评论表上有一个能够正确支持这个的索引 - 否则查询会起作用,但它确实非常低效:
create index comments_hier_idx on comments (parent_path, date_posted);
Run Code Online (Sandbox Code Playgroud)
对于任何给定的单一评论,很容易得到该评论的整个儿童评论树.只需添加一个where子句:
select id, parent_path, parent_id, comment_text, date_posted
from comments
where parent_path like '/1/%'
order by parent_path, date_posted;
Run Code Online (Sandbox Code Playgroud)
添加的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;
Run Code Online (Sandbox Code Playgroud)
管理层次结构
当然,为了使用这种方法,您必须确保parent_path在插入每个注释时正确设置.如果你移动评论(这肯定是一个奇怪的用例),你必须确保手动更新从属于移动的评论的每个评论的每个parent_path.......但这些都是相当容易跟上的事情.
如果你真的想要得到它(并且如果你的数据库支持它),你可以编写触发器来透明地管理parent_path - 我将为读者留下这个练习,但基本的想法是插入和更新触发器会触发在提交新插入之前.他们会走上树(使用parent_id外键关系),并重建相应的值parent_path.
甚至可以将其分解parent_path为一个单独的表,该表完全由注释表上的触发器管理,具有一些视图或存储过程来实现您需要的各种查询.因此,完全隔离您的中间层代码,而不需要了解或关心存储层次结构信息的机制.
当然,通过任何方式都不需要任何花哨的东西 - 通常只需将parent_path删除到表中,并在中间层中编写一些代码以确保它与所有其他字段一起正确管理你必须要管理.
施加限制
MySQL(以及其他一些数据库)允许您使用以下LIMIT子句选择数据的"页面" :
SELECT * FROM mytable LIMIT 25 OFFSET 0;
Run Code Online (Sandbox Code Playgroud)
不幸的是,在处理这样的分层数据时,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;
Run Code Online (Sandbox Code Playgroud)
相反,我们需要在我们想要施加限制的级别上进行单独的选择,然后我们将其与我们的"子树"查询一起加回以给出最终期望的结果.
像这样的东西:
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;
Run Code Online (Sandbox Code Playgroud)
注意声明limit 25 offset 0,埋在内部选择的中间.此语句将检索最新的25个"根级"注释.
[编辑:你可能会发现你需要玩一些东西来获得按照自己喜欢的方式订购和/或限制事物的能力.这可能包括在编码的层次结构中添加信息parent_path.例如:/{id}/{id2}/{id3}/您可能决定将post_date包含在parent_path的一部分中,而不是: /{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/.这样可以很容易地获得所需的订单和层次结构,代价是必须预先填充字段,并在数据更改时进行管理]
希望这可以帮助.祝好运!
| 归档时间: |
|
| 查看次数: |
2382 次 |
| 最近记录: |