Content Table

答疑表设计

答疑主要涉及 2 个表,问题表 qa_queston 和回复表 qa_reply,核心字段如下:


数据存储逻辑:

  • 问题存储到表 qa_question
  • 每个回复都存储问题 ID: question_id
  • 每个回复都保存第一级回复 ID: top_reply_id,为了方便找到回复树下的所有回复
  • 对问题进行的回复存储到表 qa_reply
    • parent_id 为 0
    • top_reply_id 为此回复的 ID
  • 对回复进行的回复存储到表 qa_reply
    • 回复是一个有层级的树状结构,所以使用了 parent_id
    • parent_id 为被回复的回复 ID
    • top_reply_id 为此回复所在第一级回复 ID

此设计的优点:

可以使用一条 SQL 语句分页查询出问题的 N 个一级回复、以及这些一级回复下所有相关的回复:

  • 查找到问题的 n 个第一级回复的 top_reply_id x
  • 查找问题的回复中所有第一级回复为 x 的回复
1
2
3
4
5
SELECT q.*, r.*
FROM qa_question q
JOIN (SELECT question_id, top_reply_id FROM qa_reply WHERE question_id = 1 AND parent_id = 0 LIMIT 0, 10) t ON t.question_id = q.id
LEFT JOIN qa_reply r ON r.question_id = q.id AND r.top_reply_id = t.top_reply_id
ORDER BY r.created_at DESC;

parent_id 为 0,说明是第一级回复。

如果回复再增加 level 属性,还可以对回复层数进行过滤,当层数很多时,可以动态请求更深层次的回复,避免一次获取太多数据。

此外,根据业务,可以对问题表增加字段如是否加精、推荐置顶、点赞数量、回复数量等,回复表增加点赞数量、被回复者的名字 (方便显示) 等,对点赞、取消点赞、点击推荐等行为记录到日志表中。


以下部分用于测试

问题和回复

1
2
3
4
5
6
7
8
9
1. 特别定制「乌龙茶」,有人跟我一样只喜欢有味道的,或者冰的饮品嘛?
1. 东方树叶,每天基本都会喝 1,2 瓶
2. 抹茶粉确实很流行,但是和我提的浓缩粉其实是两种产品
3. 炭焙的一般口味略重
4. 好处就是完全不伤胃
2. 找一个靠谱的设计师长期合作
5. 明天早上起来加你
6. 怎么联系
7. 本人做一些兼职

清空数据库

1
2
TRUNCATE TABLE qa_question;
TRUNCATE TABLE qa_reply;

问题一

1
2
3
4
5
6
INSERT INTO qa_question(id, clazz_id, user_id, user_name, content) VALUES (1, 1, 30, 'Bob', '特别定制「乌龙茶」,有人跟我一样只喜欢有味道的,或者冰的饮品嘛?');

INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (1, 1, 0, 1, 31, 'Tom', '东方树叶,每天基本都会喝 1,2 瓶');
INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (2, 1, 0, 2, 31, 'Tom', '抹茶粉确实很流行,但是和我提的浓缩粉其实是两种产品');
INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (3, 1, 2, 2, 31, 'Tom', '炭焙的一般口味略重');
INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (4, 1, 2, 2, 31, 'Tom', '好处就是完全不伤胃');

问题二

1
2
3
4
5
INSERT INTO qa_question(id, clazz_id, user_id, user_name, content) VALUES (2, 1, 30, 'Bob', '找一个靠谱的设计师长期合作?');

INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (5, 2, 0, 5, 32, 'Max', '明天早上起来加你');
INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (6, 2, 5, 5, 32, 'Max', '怎么联系');
INSERT INTO qa_reply(id, question_id, parent_id, top_reply_id, user_id, user_name, content) VALUES (7, 2, 0, 7, 32, 'Max', '本人做一些兼职');

查询问题和回复

只在第一级回复上分页,同时查询出这个回复的所有后代:

  1. 查找到问题的 n 个第一级回复的 top_reply_id x
  2. 查找问题的回复中所有第一级回复为 x 的回复
1
2
3
4
5
SELECT q.*, r.*
FROM qa_question q
JOIN (SELECT question_id, top_reply_id FROM qa_reply WHERE question_id = 1 AND parent_id = 0 LIMIT 0, 10) t ON t.question_id = q.id
LEFT JOIN qa_reply r ON r.question_id = q.id AND r.top_reply_id = t.top_reply_id
ORDER BY r.created_at DESC;

修改 question_id 查询不同问题的回复。