【问题标题】:Pre-order sorting of parents and children家长和孩子的预购排序
【发布时间】:2015-03-19 23:26:22
【问题描述】:

给定以下数据:

id  |  parent | sort
--------------------
  1 | null    | 0
  2 | null    | 1
  3 | 1       | 0
  4 | 1       | 1
  5 | 3       | 0
  6 | 5       | 0
  7 | 2       | 0

我如何进行预购排序,即首先是父母,然后是孩子,然后是孙子等等...?

我要查找的排序结果是:1、3、5、6、4、2、7

如果可能的话,我想在不使用 CTE(或我能理解的 CTE)的情况下执行此操作。我现在这样做的方式只是选择每条记录并检查“向上”以查看是否有任何父母、祖父母和曾祖父母。为没有父母的记录做一些事情(最重要的项目)并让它继续直到不再有孩子的记录更有意义,对吧?

我就是想不通……

这是对我的实际查询的过度简化,但我现在正在做的是:

SELECT ..some columns ..
FROM table t
LEFT JOIN table tparent WHERE tparent.ID = t.Parent
LEFT JOIN table tgrandparent WHERE tgrandparent.ID = tparent.Parent
LEFT JOIN table tgreatgrandparent WHERE tgreatgrandparent.ID = tgrandparent.Parent

【问题讨论】:

  • 你需要一个递归 CTE。
  • 好吧,如果我这样做,我会这样做,但是从哪里开始呢?
  • 编辑您的问题并包含您现在使用的代码。
  • 我们是否保证任何孩子的id 值总是高于他们各自的父母? IE。将6s 的父级更改为7 而不是5 将是我们不必处理的数据集?

标签: sql tsql sorting preorder


【解决方案1】:

这确实使用了 CTE,但希望我能解释一下它们的用法:

;With ExistingQuery (id,parent,sort) as (
select 1,null,0 union all
select 2,null,1 union all
select 3,1   ,0 union all
select 4,1   ,1 union all
select 5,3   ,0 union all
select 6,5   ,0 union all
select 7,2   ,0
), Reord as (
    select *,ROW_NUMBER() OVER (ORDER BY parent,sort) as rn from ExistingQuery
), hier as (
    select id,parent,'/' + CONVERT(varchar(max),rn)+'/' as part
    from Reord
    union all
    select h.id,r.parent,'/' + CONVERT(varchar(max),r.rn) + part
    from hier h
        inner join
        Reord r
            on
                h.parent = r.id
)
select
    e.*
from
    hier h
        inner join
    ExistingQuery e
        on
            h.id = e.id
where
    h.parent is null
order by CONVERT(hierarchyid,h.part)

ExistingQuery 就是您当前查询的任何内容。您应该能够将现有查询放在那里(可能带有扩展的列列表),一切都应该正常工作。

Reord 解决了我的一个问题,但可能不需要它 - 如果您的实际数据实际上是这样的,id 值的顺序确实正确,我们可以忽略 sort 然后删除 Reord 和用id 替换对rn 的引用。但是这个 CTE 这样做是为了确保父母的孩子尊重sort 列。

最后,hier CTE 是这个解决方案的核心 - 对于每一行,它都会为该行构建一个 hierachyid - 从子节点开始,沿着树向上工作,直到我们找到根。

一旦 CTE 完成,我们会返回到 ExistingQuery,这样我们就可以从那里获取数据,但可以使用 hierarchyid 执行正确的排序 - 该类型已经知道如何正确排序分层数据。

结果:

id          parent      sort
----------- ----------- -----------
1           NULL        0
3           1           0
5           3           0
6           5           0
4           1           1
2           NULL        1
7           2           0

结果显示来自hierpart 列,这可能有助于您了解 CTE 构造了什么:

id          parent      sort        part
----------- ----------- ----------- --------------
1           NULL        0           /1/
3           1           0           /1/3/
5           3           0           /1/3/6/
6           5           0           /1/3/6/7/
4           1           1           /1/4/
2           NULL        1           /2/
7           2           0           /2/5/

(您可能还想将最后的 SELECT 更改为 SELECT * from hier 以了解 CTE 的工作原理)

【讨论】:

    【解决方案2】:

    我终于投入到 CTE 并让它工作了,这里是查询的基础,如果其他人可能会遇到它。需要注意的是,sort 是一个填充字符串,从 0000000001 开始向上计数。

    WITH recursive_CTE (id, parentId, sort) 
    AS
    (
    -- CTE ANCHOR SELECTS ROOTS --
    SELECT t.ID AS id, t.Parent as parentId, t.sort AS sort
    FROM table t 
    WHERE t.Parent IS NULL
    UNION ALL
    -- CTE RECURSIVE SELECTION --
    SELECT t.ID AS id, t.Parent as parentId, cte.sort + t.sort AS sort
    FROM table t
    INNER JOIN recursive_CTE cte ON cte.id = t.Parent
    )
    
    SELECT * FROM recursive_CTE
    ORDER BY sort
    

    我相信这是使这种查询工作所需的主要部分。如果您确保达到必要的指标,它实际上非常快。

    排序是通过扩展字符串来建立的。 因此,父母将排序为“0000000001”,他的直系子将排序为“00000000010000000001”,而他的孙子将排序为“000000000100000000010000000001”等。他的兄弟姐妹从“00000000002”开始,因此在所有01记录之后。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多