mysql - 没有存储过程的树层次查询

标签 mysql

我使用的是MySQL,是否可以从下面的表结构中通过单个SQL语句获得以下结果?

目前,我可以通过在 PHP 代码中使用逻辑 while 循环来获得相同的结果。如果我能在单个 SQL 中实现,那么对于性能来说就很好了。

预期结果:

|----------+-----------------------------------------------+
|  Id (PK) +    headerANDsubheader                         + 
|----------+-----------------------------------------------+
|  1       +    A-head                                     +
|  4       +     -A-head-A1-subHead                        +
|  5       +     -A-head-A2-subHead                        +
|  6       +      --A-head-A1-subHead-A1.1-subHead         +
|  7       +      --A-head-A1-subHead-A1.2-subHead         +

Id 是主键。如果父键为 0,则表示其根级别标题。

如果ParentKey不等于0,则表示它是某人的子标题,而ParentKey是该子标题的指针。

表:Header_sub

|----------+-----------------------------------------------+------------+
|  Id (PK) +    headerANDsubheader                         + ParentKey  +
|----------+-----------------------------------------------+------------+
|  1       +    A-head                                     +   0        +
|----------+-----------------------------------------------+------------+
|  2       +    B-head                                     +   0        +
|----------+-----------------------------------------------+------------+
|  3       +    C-head                                     +   0        +
|----------+-----------------------------------------------+------------+
|  4       +    A-head-A1-subHead                          +   1        +
|----------+-----------------------------------------------+------------+
|  5       +    A-head-A2-subHead                          +   1        +
|----------+-----------------------------------------------+------------+
|  6       +    A-head-A1-subHead-A1.1-subHead             +   4        +
|----------+-----------------------------------------------+------------+
|  7       +    A-head-A1-subHead-A1.2-subHead             +   4        +
|----------+-----------------------------------------------+------------+

我正在尝试这样...

SELECT 
    CONCAT(REPEAT(' ', (COUNT(parent.subject_group_name) - 1) ), node.subject_group_name) AS name
FROM 
    major_scholastic as node,
    major_scholastic as parent
WHERE 
    node.s_gid BETWEEN parent.s_gid AND parent.parent_id
GROUP BY  node.subject_group_name
ORDER BY node.s_gid

最佳答案

您的headerANDsubheader列是某种物化路径。这允许您通过使用带有 LIKE 条件的 JOIN 来获取所有祖先(不仅仅是直接父代)。

以下查询演示了如何获取不同任务可能需要的信息:

select node.*
    , group_concat(anc.Id order by char_length(anc.headerANDsubheader)) as idPath
    , group_concat(lpad(anc.Id, 10, 0) order by char_length(anc.headerANDsubheader)) as idPathSortable
    , count(anc.Id) as depthLevel
    , concat(repeat('- ', count(anc.Id)-1), node.headerANDsubheader) as indendtedHeader
from header_sub node
join header_sub anc
    on node.headerANDsubheader like concat(anc.headerANDsubheader, '%')
group by node.Id
order by idPathSortable

结果如下所示:

| Id |             headerANDsubheader | ParentKey | idPath |                   idPathSortable | depthLevel |                    indendtedHeader |
|----|--------------------------------|-----------|--------|----------------------------------|------------|------------------------------------|
|  1 | A-head                         |         0 | 1      | 0000000001                       |          1 | A-head                             |
|  4 | A-head-A1-subHead              |         1 | 1,4    | 0000000001,0000000004            |          2 | - A-head-A1-subHead                |
|  6 | A-head-A1-subHead-A1.1-subHead |         4 | 1,4,6  | 0000000001,0000000004,0000000006 |          3 | - - A-head-A1-subHead-A1.1-subHead |
|  7 | A-head-A1-subHead-A1.2-subHead |         4 | 1,4,7  | 0000000001,0000000004,0000000007 |          3 | - - A-head-A1-subHead-A1.2-subHead |
|  5 | A-head-A2-subHead              |         1 | 1,5    | 0000000001,0000000005            |          2 | - A-head-A2-subHead                |
|  2 | B-head                         |         0 | 2      | 0000000002                       |          1 | B-head                             |
|  3 | C-head                         |         0 | 3      | 0000000003                       |          1 | C-head                             |

sqlfiddle

现在只需迈出一小步即可获得您需要的结果:

select node.Id, concat(
    repeat(' ', count(anc.Id)-1),
    repeat('-', count(anc.Id)-1),
    node.headerANDsubheader
) as indendtedHeader
from header_sub node
join header_sub anc
    on node.headerANDsubheader like concat(anc.headerANDsubheader, '%')
group by node.Id
order by group_concat(lpad(anc.Id, 10, 0) order by char_length(anc.headerANDsubheader))

结果:

| Id |                    indendtedHeader |
|----|------------------------------------|
|  1 | A-head                             |
|  4 |  -A-head-A1-subHead                |
|  6 |   --A-head-A1-subHead-A1.1-subHead |
|  7 |   --A-head-A1-subHead-A1.2-subHead |
|  5 |  -A-head-A2-subHead                |
|  2 | B-head                             |
|  3 | C-head                             |

sqlfiddle

更新:

join header_sub anc
    on node.headerANDsubheader like concat(anc.headerANDsubheader, '%')

anc 是“ancestor”的缩写。我们希望将每个节点与其所有祖先(包括其自身)连接起来。该条件可以理解为 anc.headerANDsubheader IS PREFIX OF node.headerANDsubheader。因此“A-head-A1-subHead-A1.1-subHead”将与“A-head”、“A-head-A1-subHead”和“A-head-A1-subHead-A1.1-subHead”连接”。通过 node.id 对结果进行分组,我们可以使用聚合 COUNT 来获取深度级别,并使用 GROUP_CONCAT 来生成有用的路径。不过,最好将深度和路径存储在表中,这样我们就根本不需要连接。

关于mysql - 没有存储过程的树层次查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38813091/

相关文章:

mysql:基于1小时间隔统计特定时间内访问资源的用户数

mysql - 左外连接不返回左表中的所有行?

php - 我的 MYSQL 数据库使用哪种搜索方法

mysql - 从多个表中查询行

php - 无效使用群组功能

mysql - 在 mysql 中使用 Sum (if) 的列总计

mysql - 如何重新排序 MySQL 表中的记录/行

php - 用于在表上显示数据的 MYSQL Select 查询不起作用

PHP - 存储为 BLOB 的图像显示为断开的链接

具有复合索引和持久列的 MySQL 查询优化