同一路径内的最短字符串(分支)

时间:2011-05-28 15:20:09

标签: php mysql sql

我在mysql表中有一个基于iddepthparent_idpath的树表示。此表中的每个 root 记录的深度为0parent_id != nullpath表示,基于使用0填充的ID的十六进制值。

树的每个元素都是通过指定depth = parent.depth + 1path = parent.path + hex(id)parent_id = parent.id(伪代码)构建的,例如:

id    path            depth    parent_id    assigned_user_id
------------------------------------------------------------
1     001             0        NULL         NULL
2     002             0        NULL         1
3     001003          1        1            2
4     002004          1        2            1
5     001003005       2        3            2
6     001003005006    3        5            2
7     002004007       2        4            1
8     002004008       2        4            2
9     002004009       2        4            2
10    00200400800A    3        8            2

依旧...... 问题是如何将特定用户ID的记录限制为同一分支中的最短路径。例如assigned_user_id = 2 retrive:

id    path            depth    parent_id    assigned_user_id
------------------------------------------------------------
3     001003          1        1            2
8     002004008       2        4            2
9     002004009       2        4            2

而不是:

id    path            depth    parent_id    assigned_user_id
------------------------------------------------------------
3     001003          1        1            2
5     001003005       2        3            2
6     001003005006    3        5            2
8     002004008       2        4            2
9     002004009       2        4            2
10    00200400800A    3        8            2

5 个答案:

答案 0 :(得分:2)

如果我说得对,那么排除parent_id属于所选ID的行可能就足够了。这是因为如果选择了父级和子级,则它们必须位于同一分支中。父母的路径会更短,因此可以排除孩子。

类似的东西:

SELECT * 
  FROM x 
  WHERE assigned_user_id = 2 
        AND parent_id NOT IN (SELECT id FROM x WHERE assigned_user_id = 2)

如果您有这样的树(数字是您指定的用户ID):

  A1                    G2
 / \                   / \
B2  C2                H2  I2
    | \               |   | \
    D2  E2            L1  J2 K2
                      |
                      M2

将选择B2,C2,G2和M2。不过,我仍然不确定这是不是你的意图。

答案 1 :(得分:2)

SELECT t1.*
FROM atable t1
  LEFT JOIN atable t2
    ON t2.assigned_user_id = t1.assigned_user_id AND
       t2.path = LEFT(t1.path, CHAR_LENGTH(t2.path)) AND
       t2.id <> t1.id
WHERE t1.assigned_user_id = 2
  AND t2.id IS NULL

答案 2 :(得分:1)

我会尝试这样的事情:

SELECT * FROM PATHS WHERE ASSIGNED_USER_ID = 2
AND NOT PARENT_ID IN (SELECT ID FROM PATHS WHERE ASSIGNED_USER_ID = 2)

基本上,我们的想法是为给定用户选择顶级父节点。

答案 3 :(得分:1)

背后的理念:如果A以B开头,则B比A短。也许有比“LIKE”更好的东西“以”开头“。

SELECT a.* FROM node AS a
WHERE a.assigned_user_id = ?
AND NOT EXIST
(SELECT * FROM node AS b
    WHERE b.assigned_user_id = ?
    AND LENGTH(a.path) > LENGTH(b.path) 
    AND a.path LIKE CONCAT(b.path, '%') )

两者?映射到所需的用户ID。

修改

忘记包含assigned_user_id。更改了代码。

第二次编辑

更改代码以避免b = a。

的情况

答案 4 :(得分:0)

你尝试过这样的事吗?

select child.assigned_user_id, child.id
from node as child
left join node as parent
on child.path like CONCAT(parent.path, '%')
and child.assigned_user_id = parent.assigned_user_id
and child.id <> parent.id
group by child.assigned_user_id, child.id
having max(parent.id is null) = true

(不确定它是否完全如上所述,但基本上是:在路径上保持连接以提取完整的父项列表,然后以这样的方式聚合,即只保留没有任何父项的节点按assign_user_id分组时。)