SQL-是否获得所有父母/子女?

时间:2018-11-22 15:08:43

标签: sql sql-server-2012

希望有人可以提供帮助。我已经收到了一个数据表,我需要对其进行重组和构建一个Denorm表。表结构如下

UserID     Logon    ParentID 
2344       Test1     2000
2345       Test2     2000

我遇到的问题是ParentID也是它自己的UserID,并且在同一张表中。

SELECT * FROM tbl,其中ParentID = 2000给出以下输出

UserID     Logon      ParentID 
2000       Test Team     2500

同样,其ParentID也存储为UserID。

SELECT * FROM tbl,其中ParentID = 2500给出以下输出

UserID     Logon            ParentID 
2500       Test Division     NULL

我想要一个将所有这些关系和登录信息拉成一行的查询,其输出如下所示。

 UserID   Username       Parent1        Parent2        Parent3     Parent4
 2344       Test1      Test Team      Test Division    NULL         NULL
 2345       Test2      Test Team      Test Division    NULL         NULL

用户最多可以有4位父母,在这种情况下,只有2位。有人可以帮我解决构建此问题的查询吗?

感谢任何帮助 谢谢 杰西

4 个答案:

答案 0 :(得分:2)

您基本上可以使用LEFT JOIN。如果您有静态的4父它应该工作。如果您的父母不明,则应该进行动态查询。

SELECT U1.UserId
        ,U1.UserName
        ,U2.UserName AS Parent1
        ,U3.UserName AS Parent2
        ,U4.UserName AS Parent3
        ,U5.UserName AS Parent4
FROM Users U1
LEFT JOIN Users U2 ON U1.ParentId = U2.UserId
LEFT JOIN Users U3 ON U2.ParentId = U3.UserId
LEFT JOIN Users U4 ON U3.ParentId = U4.UserId
LEFT JOIN Users U5 ON U4.ParentId = U5.UserId

编辑:附加(从列表中排除父用户):

WHERE NOT EXISTS (SELECT 1 FROM Users UC WHERE U1.UserId = UC.ParentId)

答案 1 :(得分:1)

select 
   tb1.UserId as UserId,
   tb1.UserName as UserName,
   tb2.UserName as Parent1, 
   tb3.UserName as Parent2, 
   tb4.UserName as Parent3, 
   tb5.UserName as Parent4 
from tbl t1 
left join tbl t2 on t2.UserId=t1.ParentID 
left join tbl t3 on t3.UserId=t2.ParentID 
left join tbl t4 on t4.UserId=t3.ParentID  
left join tbl t5 on t5.UserId=t4.ParentID;

您需要进行4个左联接才能获取4个父级详细信息

答案 2 :(得分:0)

为了获得所有父母或孩子,使用递归函数会获取整个层次结构,这很有效。

样品表:

CREATE TABLE #TEST
(
[Name] varchar(100),
ManagerName Varchar(100),
Number int
)

插入一些值

Insert into Test values
('a','b'), ('b','c'), ('c','d'), ('d','e'), ('e','f'), ('f','g')

创建如下的递归函数

CREATE FUNCTION [dbo].[fnRecursive] (@EmpName Varchar(100), @incremental int)
RETURNS @ret TABLE 
(

 ManagerName varchar(100),
 Number int
)
AS
BEGIN
  Declare @MgrName varchar(100)

  SET @MgrName = (Select ManagerName from test where [name] = @EmpName)

  Insert into @ret values (@MgrName, @incremental)
  if(@MgrName is not null)
  BEGIN
     SET @incremental = @incremental + 1;
     Insert into @ret
     Select ManagerName, Number  from [fnRecursive](@MgrName, @incremental)

  END

   RETURN;
END

如果此功能与表结合,则应列出所有员工的层次结构

CREATE TABLE #TEST
(
  [Name] varchar(100),
  ManagerName Varchar(100),
  Number int
)



 Insert into #TEST
 Select x.[Name], x.ManagerName,x.number from (
 select t.[Name],a.ManagerName as managerName, a.number as number  from TEST t outer apply
 (
    select * from [fnRecursive](t.[Name],1)
 ) a)
 x

Select * from #Test

如果我们在表上进行数据透视(不包括“数字”列)。假设我们将存储在“ #temp”表中,则应将所有管理器列为一列。

 DECLARE @cols AS NVARCHAR(MAX),
 @query  AS NVARCHAR(MAX);

 SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.[ManagerName] ) 
        FROM #temp c
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')



  set @query = 'select * from #temp
        pivot 
        (
            min([managername])
            for managername in (' + @cols + ')
        ) p '

  execute(@query)

但这不会将该列命名为'Parent1','Parent2',而是使用动态列名称。

下面的链接应有助于为动态数据透视表设置自定义列名

 https://stackoverflow.com/questions/16614994/sql-server-pivot-with-custom-column-names

答案 3 :(得分:0)

使用递归CTE获取级别,然后旋转以将其放入列中:

WITH cte(UserID, Logon, ParentID, ParentLogon, ParentLevel) AS
(
  SELECT UserID, Logon, ParentID, Logon,  0
  FROM users
  UNION ALL
  SELECT u.UserID, u.Logon, u.ParentID, cte.ParentLogon, ParentLevel + 1
  FROM users u
  JOIN cte ON cte.UserID = u.ParentID
)
SELECT UserId, Logon, Parent1, Parent2, Parent3, Parent4 FROM cte
PIVOT (
  MAX(ParentLogon)
  FOR ParentLevel
  IN (
    1 AS Parent1,
    2 AS Parent2,
    3 AS Parent3,
    4 AS Parent4
  )
)

请参见SQL Fiddle example