父母和孩子的预购分拣

时间:2015-01-21 12:29:42

标签: sql tsql sorting preorder

鉴于以下数据:

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

2 个答案:

答案 0 :(得分:0)

这确实使用了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并将rn的引用替换为id。但是这个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

结果显示part的{​​{1}}列,可能会帮助您了解CTE构建的内容:

hier

(您可能还希望将最终的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,以便了解该CTE的工作方式。

答案 1 :(得分:0)

我终于进入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'等。他的兄弟姐妹从'0000000002'开始,所以在所有01记录之后。