根据mysql中的路径选择

时间:2011-06-13 23:29:21

标签: php mysql sql materialized-path-pattern

我有一列id,一列parent和一列path,这是一条物化路径。

看起来像是

1  | \N | 1  
2  | 1  | 1/2  
3  | 2  | 1/2/3  
4  | 3  | 1/2/3/4  
5  | 3  | 1/2/3/5  
6  | 2  | 1/2/6  
7  | 6  | 1/2/6/7  
8  | 2  | 1/2/8  
9  | 1  | 1/9  
10 | 9  | 1/9/10  
11 | 10 | 1/9/10/11  
12 | 11 | 1/9/10/11/12  
13 | 11 | 1/9/10/11/13  
14 | 11 | 1/9/10/11/14  
15 | 14 | 1/9/10/11/14/15  
16 | 14 | 1/9/10/11/14/16  
17 | 14 | 1/9/10/11/14/17  
18 | 10 | 1/9/10/18  
19 | \N | 19  
20 | 19 | 19\20  
21 | 19 | 19\21

我需要根据此表进行一些查询。

我需要做的查询是

<小时/> 选择id 9

的所有子女
SELECT * FROM `tester` WHERE 'path' LIKE '%/9/%';  

工作正常,直到您将ID替换为1或19,因为开头没有/

SELECT * FROM `tester` WHERE 'path' LIKE '%1/%';

会选择数字以1结尾的所有行,因此,1,11,21,31,211等

SELECT * FROM `tester` WHERE 'path' LIKE '1/%';

可以正常运行第1行或第19行

所以SELECT * FROM测试人员WHERE 'path' LIKE '1/%' OR 'path' LIKE '%/1/%';
我能提出最好的建议吗?

<小时/> 选择直接子女为9但不包括子女
对于此Select * from测试人员where 'parent' = 9;会很好。

<小时/> 选择9个孩子的总计数,x深度。

所以我想最终得到一行level1, level2, level3, ... levelx或x行,代表不同的等级,

让我们假设这个例子的x是3 此示例中的行将为9, 8, 6(如果我们请求它将为3,则为第4级)

任何想法?

修改

#select count of children of specific node(5) down to a maximum of three levels, do no include the parent
SELECT COUNT(child.id) children, 
LENGTH(REPLACE(child.path, parent.path, '')) - LENGTH(REPLACE(REPLACE(child.path, parent.path, ''), '/', '')) AS LEVEL
FROM `tester` child JOIN `tester` parent ON child.path LIKE CONCAT(parent.path,'%') 
WHERE parent.id  =5 
GROUP BY LEVEL HAVING LEVEL <= 3 AND LEVEL > 0;

<小时/> **选择9个孩子的id为x级别,级别相对于9,

因此,对于此示例,我们将使用3作为x。

我们希望回来

10 | 1
11 | 2
18 | 2
12 | 3
13 | 3
14 | 3 

我再次完全失去了如何做到这一点。

修改:

#select all information, and relative level from parent of children of specific node(5) down to a maximum of three levels, do no include the parent
SELECT child.*, 
LENGTH(REPLACE(child.path, parent.path, '')) - LENGTH(REPLACE(REPLACE(child.path, parent.path, ''), '/', '')) AS LEVEL
FROM `tester` child JOIN `tester` parent ON child.path LIKE CONCAT(parent.path,'%') 
WHERE parent.id  =9 
GROUP BY id HAVING LEVEL <= 3 AND LEVEL > 0;

1 个答案:

答案 0 :(得分:1)

为了让您了解一下,这些解决方案基于字符串比较,未经过优化和优化。不能使用索引。您应该考虑以不同方式规范化表格。 (见Managing Hierarchical Data in MySQL

关于一些问题:


选择所有ID为9的孩子:

由于Path列不包含前导&amp;尾部斜杠,您需要将它们连接到路径:

SELECT * 
FROM tester
WHERE CONCAT('/', path, '/') LIKE '%/9/%';

选择9个孩子的总计数,x级别深度:

我们需要按路径中的斜杠数减去父路径中的斜杠数:

SELECT (LENGTH(c.Path) - LENGTH(REPLACE(c.Path, '/', '')))
    - (LENGTH(p.Path) - LENGTH(REPLACE(p.Path, '/', ''))) AS Level,
    COUNT(*)
FROM tester c
    JOIN tester p ON c.Parent = p.ID
WHERE CONCAT('/', path, '/') LIKE '%/9/%';
GROUP BY 1

为简单起见,我使用上面的查询来显示所有级别,如果您想要深度限制x级别,请使用以下查询中的WHERE谓词。


选择9个孩子的id为x级别,级别相对于9:

我们在Path列中搜索x个级别,同时考虑父级别:

SELECT c.*
FROM tester c
    JOIN tester p ON c.Parent = p.ID
WHERE CONCAT(
    '/',
    SUBSTRING_INDEX(
        Path, 
        '/', 
        (LENGTH(p.Path) - LENGTH(REPLACE(p.Path, '/', ''))) + 4
    ),
'/') LIKE '%/9/%'

我们正在采取的步骤:

  1. 我们需要找出父亲的深度,我们可以通过计算父亲路径中的斜杠来找到它。 (LENGTH(p.Path) - LENGTH(REPLACE(p.Path, '/', ''))
  2. 我们需要在该数字上加1,因为带有1个斜杠的路径深度为2级。
  3. 我们添加了x个所需级别。
  4. 将路径列提升到总水平,(使用SUBSTRING_INDEX功能)。
  5. 添加前导和尾随斜杠。
  6. 搜索最终字符串9。