有一些递归查询可用于查找层次结构中的所有子项或父项(例如,在员工 - 经理层次结构中),但我找不到任何同行和上面/父项的示例
EmpID FName LName MgrID
11 Sally Smith NULL
1 Alex Adams 11
2 Barry Brown 11
3 Lee Osaka 11
4 David Kennson 11
5 Eric Bender 11
7 David Lonning 11
6 Lisa Kendall 4
8 John Marshbank 4
12 Barbara ONeill 4
13 Phil Wilconkinski 4
9 James Newton 3
10 Terry OHaire 3
因此,如果我想要EmpID = 6的peer-above,那么它应该返回
EmpID FName LName MgrID
11 Sally Smith NULL grandparent/peer
1 Alex Adams 11 parent-peer
2 Barry Brown 11 parent-peer
3 Lee Osaka 11 parent-peer
4 David Kennson 11 (parent of id=6)
5 Eric Bender 11 parent-peer
7 David Lonning 11 parent-peer
6 Lisa Kendall 4 (id=6)
8 John Marshbank 4 peer
12 Barbara ONeill 4 peer
13 Phil Wilconkinski 4 peer
非常感谢任何帮助
答案 0 :(得分:0)
根据您的表当前结构的方式,实现此目的的一种方法是在递归CTE中包含路径,然后解析路径以确定某人是否属于层次结构中的该路径。
例如,
DECLARE @Table TABLE
(
EmpID INT NOT NULL,
FName VARCHAR(20) NOT NULL,
LName VARCHAR(20) NOT NULL,
MgrID INT
);
INSERT @Table (EmpID, FName, LName, MgrID)
VALUES
(11, 'Sally', 'Smith', NULL),
(1, 'Alex', 'Adams', 11),
(2, 'Barry', 'Brown', 11),
(3, 'Lee', 'Osaka', 11),
(4, 'David', 'Kennson', 11),
(5, 'Eric', 'Bender', 11),
(7, 'David', 'Lonning', 11),
(6, 'Lisa', 'Kendall', 4),
(8, 'John', 'Marshbank', 4),
(12, 'Barbara', 'ONeill', 4),
(13, 'Phil', 'Wilconkinski', 4),
(9, 'James', 'Newton', 3),
(10, 'Terry', 'OHaire', 3);
WITH CTE AS
(
SELECT EmpID,
FName,
LName,
MgrID,
Tree = CAST(EmpID AS VARCHAR(255)),
Lvl = 1
FROM @Table
WHERE MgrID IS NULL
UNION ALL
SELECT T.EmpID,
T.FName,
T.LName,
T.MgrID,
CAST(CTE.Tree + '-' + CAST(T.EmpID AS VARCHAR(255)) AS VARCHAR(255)), -- Your paths will look like '11-4-6'.
CTE.Lvl + 1
FROM CTE
JOIN @Table T ON T.MgrID = CTE.EmpID
)
SELECT EmpID,
FName,
LName,
MgrID,
Lvl,
Direct = CASE MIN(CASE WHEN EmpID = Item THEN 1 ELSE 2 END) WHEN 1 THEN 'Direct' ELSE 'Peer' END
FROM CTE
JOIN
(
SELECT Item
FROM CTE
CROSS APPLY dbo.[DelimitedSplit8K](Tree, '-') D
WHERE CTE.EmpID = 6
) D ON D.Item = CTE.EmpID
OR D.Item = CTE.MgrID
GROUP BY EmpID, FName, LName, MgrID, Lvl;
这使用拆分功能来分解路径并仅显示该路径中的人员。可以找到分割函数here,如下所示:
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;