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