我有以下简化的数据库表结构,用于类似旧票证的系统。
messages
id INT
parent_id INT
content TEXT
answer TEXT
...
在列表上,我显示了所有消息。单击消息后,我将显示其答案等。
问题是,现在我需要创建与此消息相关的所有parents和children的列表结构,以及此消息在树中的位置。 如何从数据库中检索那些?
我正在使用Laravel,但是原始SQL也会帮助我找到方向。
╔════╦═══════════╦════════════════════════╦═══════ ══════════╗║id║parent_id║内容║答案║╠════╬═══════════╬════════════════════════╬═══════ ══════════╣║1║NULL║嗨,我有问题║我帮不上忙║║2║1║问题仍然存在║好的,这是什么? ║║3║2║没关系,我明白了║哦,好。 ║║4║3║问题又回来了║哪个问题? ║║5║4 again同样的问题║...║╚════牛皮═══════════牛皮════════════════════════牛皮════════════════════════ ══════════╝
当显示带有id = 4
的消息时,我应该能够显示以下列表:
消息历史记录:-嗨,我有问题-问题仍然存在-没关系,我明白了-问题又回来了-再次出现相同的问题
我只能为每个父母和孩子想到一个循环和几个SQL查询执行,这看起来像代码的味道。
如Daan所说,这个问题似乎与How to create a MySQL hierarchical recursive query重复。
但是,我决定不删除它,因为Ravan只是使用Laravel的方法回答了该问题,该方法可以帮助我解决问题,所以我将其留在此处以供将来参考。
由于您正在执行分层操作,因此应使用一种策略来从数据库中保存和检索此数据。
[一种方法是使用Nested Set Model,这可以使它更容易。Laravel有一个很好的软件包,称为etrepat/baum,它也解释了它的工作方式,我引用:
可视化嵌套集的工作方式的一种简单方法是考虑围绕所有对象的父实体它的孩子,以及它周围的父母,等等。所以这棵树:
root
|_ Child 1
|_ Child 1.1
|_ Child 1.2
|_ Child 2
|_ Child 2.1
|_ Child 2.2
可以像这样可视化:
___________________________________________________________________
| Root |
| ____________________________ ____________________________ |
| | Child 1 | | Child 2 | |
| | __________ _________ | | __________ _________ | |
| | | C 1.1 | | C 1.2 | | | | C 2.1 | | C 2.2 | | |
1 2 3_________4 5________6 7 8 9_________10 11_______12 13 14
| |___________________________| |___________________________| |
|___________________________________________________________________|
数字代表左右边界。该表然后可能看起来像这样:
id | parent_id | lft | rgt | depth | data
1 | | 1 | 14 | 0 | root
2 | 1 | 2 | 7 | 1 | Child 1
3 | 2 | 3 | 4 | 2 | Child 1.1
4 | 2 | 5 | 6 | 2 | Child 1.2
5 | 1 | 8 | 13 | 1 | Child 2
6 | 5 | 9 | 10 | 2 | Child 2.1
7 | 5 | 11 | 12 | 2 | Child 2.2
要获得parent节点的所有子代,您
SELECT * WHERE lft IS BETWEEN parent.lft AND parent.rgt
要获得孩子人数,是
(right - left - 1)/2
要获得一个节点及其所有祖先,请回到根目录,您
SELECT * WHERE node.lft IS BETWEEN lft AND rgt
如您所见,查询将是递归的,并且在普通的树木突然变得很快。漂亮,不是吗?