什么是让所有孩子都在树上的正确查询?

时间:2010-03-25 19:25:07

标签: mysql database

假设我有以下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中使用一个函数。我不认为像这样简单的东西我们需要一个完整的功能。

也许我只是愚蠢而没有看到某种明显的解决方案。

另外,随意改变结构。

4 个答案:

答案 0 :(得分:2)

你应该考虑为这种数据结构使用嵌套集

有关实施和使用的详细信息,请参阅http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

答案 1 :(得分:1)

Mysql有a good article for you

  

简介

     

大多数用户曾经在SQL数据库中处理过分层数据,毫无疑问,他们了解到分层数据的管理不是关系数据库的用途。关系数据库的表不是分层的(如XML),而只是一个平面列表。分层数据具有父子关系,这种关系在关系数据库表中无法自然表示。

     

出于我们的目的,分层数据是一个数据集合,其中每个项目都有一个父项和零个或多个子项(除了根项目,没有父项)。可以在各种数据库应用程序中找到分层数据,包括论坛和邮件列表线程,业务组织图表,内容管理类别和产品类别。出于我们的目的,我们将使用虚构电子商店中的以下产品类别层次结构:

答案 2 :(得分:0)

邻接列表只会帮助您获得错误的结果。

a.d.f和b.d.c使得四个节点与d“相邻”,但a.d.c和b.d.f都不存在。但是,关闭邻接名单会有效地假装他们愿意。

所以你的查询确实需要像“...... WHERE ENDSWITH(domain,< parameter>)。

您可能遇到的问题是此查询始终需要全表扫描。

也许这可以通过创建第二个表(domain1,domain2)来解决,该表只表示“domain1是domain2的子域”。您可以使用在基表的每次更新时运行的触发器或sprocs来更新此表。插入“a.b.c.d”在第二个表中插入三行:(a.b.c.d,b.c.d),(a.b.c.d,c.d),(a.b.c.d,d)。

现在可以将您的查询编写为两者之间的连接,如果适当的索引到位,则可以足够快地运行。

修改

但这种方法存在严重问题。如果再次删除a.b.c.d,那么其他三行也应如此,除非当然存在一些未删除的x.b.c.d行...

答案 3 :(得分:0)

解决方案很简单,虽然效率很高。

我修改了表结构如下:

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语法,但是你明白了。