可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've got a 'Task' table with the following columns (the TaskOrder is for ordering the children within the scope of the parent, not the entire table):
TaskId
ParentTaskId
TaskName
TaskOrder
I've got this CTE query to return all the rows:
with tasks (TaskId, ParentTaskId, [Name]) as
(
select parentTasks.TaskId,
parentTasks.ParentTaskId,
parentTasks.[Name]
from Task parentTasks
where ParentTaskId is null
union all
select childTasks.TaskId,
childTasks.ParentTaskId,
childTasks.[Name]
from Task childTasks
join tasks
on childTasks.ParentTaskId = tasks.TaskId
)
select * from tasks
This query returns all the tasks ordered by their level as you'd expect. How can I change it to order the results into their hierarchy order as below?
- Task 1
-- Task 1 Subtask 1
-- Task 1 Subtask 2
- Task 2
- Task 3
Thanks.
Edit: The answer should work with an unlimited numbr of levels.
回答1:
One way you could do this is to add a hierarchy column that has all previous IDs in a list:
with tasks (TaskId, ParentTaskId, [Name], TaskIdList) as
(
select parentTasks.TaskId,
parentTasks.ParentTaskId,
parentTasks.[Name],
parentTasks.TaskId
from Task parentTasks
where ParentTaskId is null
union all
select childTasks.TaskId,
childTasks.ParentTaskId,
childTasks.[Name],
tasks.TaskIdList + '.' + childTasks.TaskId
from Task childTasks
join tasks
on childTasks.ParentTaskId = tasks.TaskId
)
select TaskId, ParentTaskId, [Name] from tasks
order by TaskIdList
Note that this assumes that TaskId is a string-based ID. If not, you should cast it to a varchar before concatenating it.
回答2:
Solved the problem using a variation of Mark's method, but I'm not retaining the node path in every node, so I can more easily move them around the tree. Instead I changed my 'OrderBy' column from an int to varchar(3) left-padded with zeros so I can concatenate them into a master 'OrderBy' for all the rows returned.
with tasks (TaskId, ParentTaskId, OrderBy, [Name], RowOrder) as
(
select parentTasks.TaskId,
parentTasks.ParentTaskId,
parentTasks.OrderBy,
parentTasks.[Name],
cast(parentTasks.OrderBy as varchar(30)) 'RowOrder'
from Task parentTasks
where ParentTaskId is null
union all
select childTasks.TaskId,
childTasks.ParentTaskId,
childTasks.OrderBy,
childTasks.[Name],
cast(tasks.RowOrder + childTasks.OrderBy as varchar(30)) 'RowOrder'
from Task childTasks
join tasks
on childTasks.ParentTaskId = tasks.TaskId
)
select * from tasks order by RowOrder
This returns:
TaskId ParentTaskId OrderBy Name RowOrder
---------------------------------------------------------------------------
1 NULL 001 Task One 001
15 1 001 Task One / Task One 001001
2 NULL 002 Task Two 002
7 2 001 Task Two / Task One 002001
14 7 001 Task Two / Task One / Task One 002001001
8 2 002 Task Two / Task Two 002002
9 8 001 Task Two / Task Two / Task One 002002001
10 8 002 Task Two / Task Two / Task Two 002002002
11 8 003 Task Two / Task Two / Task Three 002002003
3 NULL 003 Task Three 003
4 NULL 004 Task Four 004
13 4 001 Task Four / Task One 004001
5 NULL 005 Task Five 005
6 NULL 006 Task Six 006
17 NULL 007 Task Seven 007
18 NULL 008 Task Eight 008
19 NULL 009 Task Nine 009
21 19 001 Task Nine / Task One 009001
20 NULL 010 Task Ten 010
It doesn't allow for an unlimited hierarchy (max 10 levels / max 1000 children per parent node - if I'd started the OrderBy at 0) but more than enough for my needs.
回答3:
You don't need all that union stuff, I think this should work:
select
TaskId,
ParentTaskId,
[Name],
COALESCE(ParentTaskId, TaskId) as groupField
from
task
order by
COALESCE(ParentTaskId, TaskId), ParentTaskId, TaskId
回答4:
Since you don't specify "ORDER BY", how do you expect that it returns them in any particular order (other than hoping the query analyzer will work in some expected fashion?).
If you want it in ParentTaskId, TaskId order, then select the TaskId as ParentTaskId and NULL as TaskId in the first UNION element; then
ORDER BY ParentTaskId, TaskId?