[英]What is the proper query to get all the children in a tree?
可以说我具有以下MySQL结构:
CREATE TABLE `domains` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`domain` CHAR(50) NOT NULL,
`parent` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MYISAM AUTO_INCREMENT=10 DEFAULT CHARSET=latin1
insert into `domains`(`id`,`domain`,`parent`) values (1,'.com',0);
insert into `domains`(`id`,`domain`,`parent`) values (2,'example.com',1);
insert into `domains`(`id`,`domain`,`parent`) values (3,'sub1.example.com',2);
insert into `domains`(`id`,`domain`,`parent`) values (4,'sub2.example.com',2);
insert into `domains`(`id`,`domain`,`parent`) values (5,'s1.sub1.example.com',3);
insert into `domains`(`id`,`domain`,`parent`) values (6,'s2.sub1.example.com',3);
insert into `domains`(`id`,`domain`,`parent`) values (7,'sx1.s1.sub1.example.com',5);
insert into `domains`(`id`,`domain`,`parent`) values (8,'sx2.s2.sub1.example.com',6);
insert into `domains`(`id`,`domain`,`parent`) values (9,'x.sub2.example.com',4);
在我看来,这足以模拟一个简单的树结构:
.com
|
example
/ \
sub1 sub2
ECT
我的问题是,给sub1.example.com我想知道sub1.example.com的所有子级,而不在我的代码中使用多个查询。
我曾尝试将表自身连接起来,并尝试使用子查询,但我想不出能揭示所有子代的任何东西。
在工作中,我们使用MPTT来按层次结构排列域/子域的列表,但是,我觉得有一种更简便的方法。
我做了一些挖掘,有人做了类似的事情,但是他们需要在MySQL中使用一个函数。 我认为对于像这样的简单事情,我们不需要完整的功能。
也许我只是愚蠢的,没有看到某种明显的解决方案。
另外,请随时更改结构。
您应该考虑将嵌套集用于此类数据结构
有关实现和用法的详细信息,请参见http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
Mysql有一个很好的文章给你
介绍
大多数用户一次或一次都在处理SQL数据库中的层次结构数据,并且毫无疑问地了解到,层次结构数据的管理不是关系数据库的目的。 关系数据库的表不是分层的(如XML),而仅仅是一个平面列表。 层次结构数据具有父子关系,该父子关系在关系数据库表中自然无法表示。
就我们的目的而言,分层数据是数据的集合,其中每个项目都有一个父项,零个或多个子项(但根项除外,后者没有父项)。 分层数据可以在各种数据库应用程序中找到,包括论坛和邮件列表线程,业务组织结构图,内容管理类别和产品类别。 为了我们的目的,我们将使用虚构的电子商店中的以下产品类别层次结构:
邻接表只会帮助您得到错误的结果。
adf和bdc使四个节点与d“相邻”,但是adc和bdf都不存在。 但是,关闭邻接表将有效地假装成他们这样做。
因此,您的查询确实确实需要类似“ ... WHERE ENDSWITH(domain,<parameter>)”。
您可能的问题是此查询将始终需要全表扫描。
也许可以通过创建第二个表(domain1,domain2)解决此问题,该表仅表示“ domain1是domain2的子域”。 您可以使用在基表的每次更新上运行的触发器或存储库来更新此表。 插入“ abcd”会在第二个表中插入三行:(abcd,bcd),(abcd,cd),(abcd,d)。
现在可以将查询编写为两者之间的联接,如果有适当的索引,该联接将足够快地运行。
编辑
但是这种方法存在严重的问题。 如果abcd被再次删除,那么其他三行也应该删除,除非当然仍然存在一些未删除的xbcd行...
该解决方案很简单,尽管在效率方面存在争议。
我修改了表结构,如下所示:
CREATE TABLE `domains` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`domain` CHAR(50) NOT NULL,
`level` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MYISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1
级别与树的深度有关。
样本数据:
insert into `domains`(`id`,`domain`,`level`) values (1,'.com',0);
insert into `domains`(`id`,`domain`,`level`) values (2,'example.com',1);
insert into `domains`(`id`,`domain`,`level`) values (3,'sub1.example.com',2);
insert into `domains`(`id`,`domain`,`level`) values (4,'sub2.example.com',2);
insert into `domains`(`id`,`domain`,`level`) values (5,'s1.sub1.example.com',3);
insert into `domains`(`id`,`domain`,`level`) values (6,'s2.sub1.example.com',3);
insert into `domains`(`id`,`domain`,`level`) values (7,'sx1.s1.sub1.example.com',4);
insert into `domains`(`id`,`domain`,`level`) values (8,'sx2.s2.sub1.example.com',4);
insert into `domains`(`id`,`domain`,`level`) values (9,'x.sub2.example.com',3);
insert into `domains`(`id`,`domain`,`level`) values (10,'t.sx1.s1.sub1.example.com',5);
假设我们给了sub1.domain.com,我们想知道其所有子级,查询非常简单:
SELECT * FROM domains WHERE domain LIKE "%.sub1.example.com" ORDER BY level;
当然,如果我们希望在结果集中使用sub1.example.com,我们可以这样做:
SELECT * FROM domains WHERE domain LIKE "%sub1.example.com" ORDER BY level;
从结果集中,我们得到给定孩子的所有孩子的列表。
删除一个子项(以及所有关联的子项)很简单,并且查询非常相似
DELETE FROM domains WHERE domain LIKE "%sub1.example.com";
插入很容易,它只需要2个查询(假设用户有一个下拉框并选择了父级):
SELECT level FROM domains WHERE domain = "sub2.example.com";
INSERT INTO domains (domain, level) VALUES ($sub + ".sub2.example.com", $level+1)
请原谅PHP + MySQL的混合语法,但是您明白了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.