问题
I have a table which stores company information and their parent company in a regular hierarchical manner, with companyid, parentid and name.
I just learn CTE query in Sql Server and write this query
WITH tableR (ParentCompanyID, CompanyID, Levels)
AS
(
-- Anchor member definition
SELECT e.ParentCompanyID, e.CompanyID, 0 As Levels
FROM tblCompany AS e
WHERE ParentCompanyID in (9)
UNION ALL
-- Recursive member definition
SELECT e.ParentCompanyID, e.CompanyID, Levels + 1
FROM tblCompany AS e
INNER JOIN tableR AS d
ON e.ParentCompanyID = d.CompanyID
)
-- Statement that executes the CTE
SELECT tabler.Levels, tableR.CompanyID, (left('--------------', (tabler.Levels* 2)) + c.CompanyName) as CName,c.ParentCompanyID
FROM tableR Left join tblcompany c on tableR.CompanyId=c.CompanyID
This works fine, except that it first list the Child of ID=9, then it list 1st level child and then level 2 .. and so on, but what I need is to have Child data come just under their parent, so
L0
L1
L2
L1-1
L2-1
....
Is it possible to do? Because if not then I have to do it recursively in C# code I am using.
I try this as well
WITH tableR (ParentCompanyID, CompanyID, Levels, RowNumber)
AS
(
-- Anchor member definition
SELECT e.ParentCompanyID, e.CompanyID, 1 As Levels, CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx)) as RowNumber
FROM tblCompany AS e
WHERE ParentCompanyID in (9)
UNION ALL
-- Recursive member definition
SELECT e.ParentCompanyID, e.CompanyID, Levels + 1, CAST(Concat(d.RowNumber, CAST((Row_Number() Over (Order by e.CompanyName) ) as VARCHAR(MAX)) ) as VARCHAR(MAX)) as RowNumber
FROM tblCompany AS e
INNER JOIN tableR AS d
ON e.ParentCompanyID = d.CompanyID
)
-- Statement that executes the CTE
SELECT tabler.Levels, RowNumber, tableR.CompanyID, (left('--------------', ((tabler.Levels - 1)* 2 )) + c.CompanyName) as CName,c.ParentCompanyID
FROM tableR Left join tblcompany c on tableR.CompanyId=c.CompanyID order by RowNumber
But it fails with if any Level has more than 9 records.
回答1:
Try this solution:
DECLARE @Company TABLE
(
CompanyID INT PRIMARY KEY,
Name NVARCHAR(50) NOT NULL,
ParentCompanyID INT NULL
);
INSERT @Company (CompanyID,Name,ParentCompanyID)
VALUES
(8,N'Tomaten',NULL),
(9,N'NON ĂNŞI chars',NULL),
(10,N'Bananen',NULL),
(11,N'Child #1',9),
(12,N'Child #2',9),
(13,N'Child #1.1',11),
(14,N'Child #1.2',11);
DECLARE @ParentCompanyID INT = 9;
WITH RecComp
AS
(
SELECT crt.CompanyID,
crt.Name,
crt.ParentCompanyID,
1 AS Lvl,
N'/' + CONVERT(NVARCHAR(4000),crt.CompanyID) + N'/' AS CompanyNode_AsChar
FROM @Company crt
WHERE crt.ParentCompanyID = @ParentCompanyID
UNION ALL
SELECT cld.CompanyID,
cld.Name,
cld.ParentCompanyID,
prt.Lvl + 1,
prt.CompanyNode_AsChar + CONVERT(NVARCHAR(4000), cld.CompanyID) + N'/'
FROM RecComp prt -- parent
INNER JOIN @Company cld ON prt.CompanyID = cld.ParentCompanyID
)
SELECT *,
CONVERT(HIERARCHYID, CompanyNode_AsChar) AS CompanyNode
FROM RecComp
ORDER BY CompanyNode;
Results:
CompanyID Name ParentCompanyID Lvl CompanyNode_AsChar CompanyNode
--------- ---------- --------------- --- --------------------- -----------
11 Child #1 9 1 /11/ 0xAE
13 Child #1.1 11 2 /11/13/ 0xAF6C
14 Child #1.2 11 2 /11/14/ 0xAF74
12 Child #2 9 1 /12/ 0xB2
Note: SQL Azure Supports Hierarchyid Data Type
回答2:
Well, the thing ios, you have no ORDER BY clause.
Why not at least try
-- Statement that executes the CTE
SELECT tabler.Levels, tableR.CompanyID, (left('--------------', (tabler.Levels* 2)) + c.CompanyName) as CName,c.ParentCompanyID
FROM tableR Left join tblcompany c on tableR.CompanyId=c.CompanyID
ORDER BY tableR.Levels
That said, the display/UI sectioin should probably be taken car of by the UI output, and not by your query.
This almost seems like you wish to diusplay it in a TreeView rather.
回答3:
Okay, finally I found a solution to issue. In order to get critics and if I am right to help other here it is
WITH tableR (ParentCompanyID, CompanyID, Levels, RowNumber, RowNumber2)
AS
(
-- Anchor member definition
SELECT e.ParentCompanyID, e.CompanyID, 1 As Levels, CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx)) as RowNumber,
CAST(
(Left('000', 3-Len(CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx)))) + CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx)))
As VARCHAR(MAX)
) AS RowNumber2
FROM tblCompany AS e
WHERE ParentCompanyID in (370)
UNION ALL
-- Recursive member definition
SELECT e.ParentCompanyID, e.CompanyID, Levels + 1, CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx)) as RowNumber1,
CAST(
Concat(d.RowNumber2,
Left('000', 3-Len(CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx)))),
CAST((Row_Number() Over (Order by e.CompanyName) ) as Varchar(MAx))
) as VARCHAR(MAX)) as RowNumber2
FROM tblCompany AS e
INNER JOIN tableR AS d
ON e.ParentCompanyID = d.CompanyID
)
-- Statement that executes the CTE
SELECT tabler.Levels, RowNumber, RowNumber2, tableR.CompanyID, (left('--------------', ((tabler.Levels - 1)* 2 )) + c.CompanyName) as CName,c.ParentCompanyID
FROM tableR Left join tblcompany c on tableR.CompanyId=c.CompanyID order by RowNumber2, CName
Now, here is the explanation:
- I add a ROW_NUMBER Function of SQL Server this simply add the counter to each row of query, so it add seperate counter to Anchor query and Recursive query.
- But as we have to arrange them, in order I append Parent/Anchor query value to Child, so Anchor query goes 1, 2, 3.. but child goes 11, 12 ... 21 ...
- Then I Cast them as String, because in String Order you will have 1, 2, 21, 3 rather than 1, 2, 3, 21 .. so that works fine for me.
Problem Known: it get bowled over when you hit Anchor query output > 10 rows, or infact any row more than 10 rows, as then anchor query give ID as 11, and child goes 111 and confuse the output.
Solution for above problem: I modify my query to use LEFT and append 000, so if I see that I can max of 100 child I put 3 zero if you see 4 then use 0000 and change 3 to 4 in query.
BUT: I strongly recommend answer above as that is the way to do it.
回答4:
I would like to share this
if you want to order de data... alphabetic and Child data come just under their parent.. create a baseCTE, use row_number instead of CompanyID, call the Anchor query from Base CTE
BASE CTE
ROW_NUMBER() OVER ( PARTITION BY ParentCompanyID ORDER BY CompanyName) as [row_number]
Anchor query
N'/' + CONVERT(NVARCHAR(4000),[row_number]) + N'/' AS CompanyNode_AsChar
Recursive query
prt.CompanyNode_AsChar + CONVERT(NVARCHAR(4000), [row_number]) + N'/'
来源:https://stackoverflow.com/questions/18770958/cte-query-how-to-make-them-in-order