【问题标题】:MySQL Order By HierarchyMySQL 按层次结构排序
【发布时间】:2015-06-10 22:52:31
【问题描述】:

我问了这个问题previously,但这是针对两层树的,我得到的解决方案非常有效。

我现在有一个多级树(最多 3 个,但假设将来可能会有更多。

我的代码目前如下所示:

SELECT * FROM fin_document AS finl

LEFT OUTER JOIN fin_document AS finl2
ON finl2.id = finl.parent_line_id

ORDER BY
CASE WHEN finl2.ordinal IS NULL THEN finl.ordinal
ELSE concat(finl2.ordinal,'-',finl.ordinal) END

让我们假设一棵与之前类似的树:

(id)  (Item)    (#)  (parent_line_id)
1234 - Car    -  1 -  null
0000 - Boat   -  2 -  null
2222 - House  -  4 -  null
6545 - Bike   -  5 -  null
6547 - Wheels -  0 -  1234
4442 - Bed    -  1 -  2222
1474 - Sink   -  0 -  2222
9456 - Tires  -  0 -  6547                  *New item, child of wheels
8975 - L.Nuts -  1 -  6547                  *New item, child of wheels

哦,#列是“序数”

那么,我如何才能让不止一位家长对此进行正确排序?

正确的排序应该如下所示:

(id)  (Item)    (#)  (parent_line_id)
1234 - Car    -  1 -  null
6547 - Wheels -  0 -  1234
9456 - Tires  -  0 -  6547
8975 - L.Nuts -  1 -  6547 
0000 - Boat   -  2 -  null
2222 - House  -  4 -  null
1474 - Sink   -  0 -  2222
4442 - Bed    -  1 -  2222
6545 - Bike   -  5 -  null

注意:我无法更改表格。我只能从表中提取数据,因为这些表是由我们使用的另一家公司管理的。我知道如果有更多的孩子,他们会变得越来越复杂,但我认为我的公司将使用这个的孩子不会超过 3-4 个。不幸的是,由于这种复杂性,这就是为什么我不得不回到这里再次询问:(

【问题讨论】:

  • 这将是一次重组,所以我将其作为评论而不是答案。邻接表模型相当不灵活,并且随着大小的增长变得更加复杂。更好的选择(不是最好的)是使用here 描述的嵌套集合模型。最好的选择是为您的结构使用图表,为数据使用 rdbms,但这是另一个主题。
  • 我假设您的意思是原始表格的结构。这次我忘了提,但我无法更改表格。我只能从表中提取数据,因为这些表是由我们使用的另一家公司管理的。感谢您提醒我这一点。

标签: mysql sorting hierarchy


【解决方案1】:

希望您不是在寻找无需修改即可与 N 深层次结构一起使用的东西。

然而,这应该是相当简单的扩展。

SELECT id,
    item,
    o,
    parent_line_id
FROM (
    SELECT *,
        1 AS parentage,
        o AS rank
    FROM table1
    WHERE parent_line_id IS NULL

    UNION ALL

    SELECT t2.id,
        t1.item,
        t1.o,
        t1.parent_line_id,
        2 AS parentage,
        t2.o AS rank
    FROM table1 t1
    INNER JOIN table1 t2 ON t1.parent_line_id = t2.id
        AND t2.parent_line_id IS NULL

    UNION ALL

    SELECT t3.id,
        t1.item,
        t1.o,
        t1.parent_line_id,
        3 AS parentage,
        t3.o AS rank
    FROM table1 t1
    INNER JOIN table1 t2 ON t1.parent_line_id = t2.id
        AND t2.parent_line_id IS NOT NULL
    INNER JOIN table1 t3 ON t2.parent_line_id = t3.id
    ) q
ORDER BY rank ASC,
    parentage ASC,
    o ASC;

demo here

基本前提是我们识别出所有的无父项,并赋予它们的父级为1。

然后我们识别他们的孩子,给他们 2 的父母,他们的孩子获得 3 的父母。

为了排序,它们都继承了第一个父序数。

可能还有其他方法可以做到这一点,我什至可能会寻找它们,但与此同时 - 这很有效。

【讨论】:

  • 嗯......它有点工作。我在我制作的测试表中进行了尝试,它以正确的顺序放置了一些东西,但由于某种原因,它移动和移除了物品等。例如,父行是正确的,因为 id、name、ordinal、parent_id 都应该是一样的。但是,子元素都是混乱的-名称替换了parent_id,id变成了最顶层父(包括孙子)的parent_id,名称也变成了parent_id......也订购。老实说,我也不知道您的代码中发生了什么。
  • @user3669901 能否请您更新我发布的演示,使用您提供的输出不正确的数据?
  • 您发布的演示完全不适合我。
  • @user3669901 sqlfiddle 有时可能不可靠 - 目前看起来仍然处于关闭状态:/
【解决方案2】:

这是处理任意深度的dirty trick

SELECT a.k, b.*
FROM (
    SELECT a.id, a.k
    FROM (
        SELECT CONCAT(LEFT(
               (@c := @previous_id <> b.id OR @previous_id IS NULL)
               & (@id := IF( @c, b.id, (SELECT parent_line_id FROM fin_document WHERE id = @id)))
               & (@num := LPAD(IF( @c, b.num, (SELECT num FROM fin_document WHERE id = @id)), 5, ' '))
               & (@key := IF( @c, @num, CONCAT(@num, '', @key) ))
               & (@previous_id := b.id),0),@key) k,
               b.id
        FROM fin_document a
        STRAIGHT_JOIN ( SELECT @previous_id := NULL, id, num FROM fin_document ) b ) a
            WHERE k IS NOT NULL
    ORDER BY id, LENGTH(k) DESC) a
JOIN fin_document b ON a.id = b.id
GROUP BY a.id
ORDER BY k;

fiddle(不知道为什么列 k 显示不正确。列 k 代表排序键,​​并以与原始查询相似的格式构建) 此外,它需要指数级的执行时间。所以它可能不是你想要的。

【讨论】:

  • Mysql 不一定会从左到右评估选择字段,因此无法保证变量按照您希望的顺序进行评估。这很可能会在不同的版本或安装中返回不同的结果。
  • @pala_,哇,谢谢。我不知道未定义的执行顺序。我将它们合并到一个字段中。你认为它现在有效吗?
  • 它可能会 - 我通常尝试通过将所有变量操作放在丑陋的嵌套条件语句中来强制执行顺序。
【解决方案3】:

**

邻接列表(层次结构) - 基于级别的排序/顺序

**

对于任何搜索的人来说,如果你有一个 Adjacency list ID->Parent 结构,你实际上可以通过使用 Path 枚举模型来维护一个 LEVEL BASED SORTING :)

在数据表中,您将拥有父列和排序值列

为了生成具有正确排序的层次结构,我使用以下 CTE:

CTE_Topic_Details AS
(
SELECT Topic_id, CONVERT(Topic_id, CHAR(100)) as path, Topic_name as TopFullN,CONVERT(Topic_sort, CHAR(100)) as SortGPS
FROM topic
WHERE Topic_Topic_id IS NULL
UNION ALL
SELECT e.Topic_id, CONCAT(d.path, ".", CONVERT(e.Topic_id, CHAR(20))),  CONCAT(d.TopFullN, ">", e.Topic_name), CONCAT(d.SortGPS, ".", CONVERT(e.Topic_sort, CHAR(20)))
FROM topic e
JOIN CTE_Topic_Details d ON e.Topic_Topic_id = d.Topic_id
)

很好,不是吗!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 1970-01-01
    • 2016-05-21
    • 2011-06-01
    • 2022-01-08
    • 2020-12-25
    • 1970-01-01
    相关资源
    最近更新 更多