繁体   English   中英

将所有子代放入树中的正确查询是什么?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM