如何用层次结构转换表?

时间:2020-03-11 10:43:29

标签: sql-server

我有一个带有两列的表,看起来像这样

enter image description here

我想将其转换为这种格式,在这种格式下,经理层次结构下的所有用户ID都应该映射。如果用户ID没有管理员ID,则需要使用相同的用户ID作为管理员ID

enter image description here

任何帮助,即使我的方向正确,也将非常有帮助。谢谢

2 个答案:

答案 0 :(得分:0)

这只是在层次结构上进行递归的经典案例:

declare @table table (ManagerId int, userId int);
insert @table values 
    (4,1), (4,2), (4,3), -- report to 4
    (5,4), -- report to 5
    (6,5), -- report to 6
    (7,6); -- report to 7

with traverse as (

    select     managerId, userId
    from       @table t

    -- Union all in a cte selecting from 
    -- the cte itself initiates recursion.
    union all  
    select     nxt.managerId, cur.userId
    from       traverse cur
    join       @table nxt on cur.managerId = nxt.userId 
    where      nxt.managerId is not null
)


select     isnull(managerId, userId) managerId, userId
from       traverse             
order by   managerId, userId
option     (maxrecursion 1000) -- change if you're getting a max recursion error

答案 1 :(得分:0)

当我为您设计解决方案时,我看到了很大的进步。它可能未适应最新的更改。请看看。

Option Explicit

Enum Nws                        ' worksheet locations
    NwsFirstDataRow = 2
    NwsStaff = 1                ' 1 = column A
    NwsSuper                    ' no value = (preceding + 1)
                                ' the above columns need not be adjecent
    NwsManager = 5              ' { blank columns for output
    NwsUserID                   ' { function as your sample
End Enum

Sub SortHierarchy()
    ' Variatus @STO 12 Mar 2020

    Dim Rng As Range
    Dim Arr As Variant
    Dim i As Long                           ' index to Arr()
    Dim StaffID As Variant                  ' can be alphanumeric
    Dim Rl As Long                          ' last row
    Dim R As Long                           ' current row
    Dim Spike() As String
    Dim n As Long                           ' index to Spike()
    Dim Sp() As String                      ' split of Spike(n)


    Application.ScreenUpdating = False

    With Worksheets("Sheet1")               ' modify tab name
        Rl = .Cells(.Rows.Count, NwsStaff).End(xlUp).Row
        ' both columns should be of equal length
        Set Rng = .Range(.Cells(NwsFirstDataRow, NwsStaff), _
                         .Cells(Rl, NwsSuper))
        SortData Rng, NwsSuper
        Arr = Rng.Columns(NwsSuper).Value
        ReDim Spike(UBound(Arr) - 1)

        For R = NwsFirstDataRow To Rl
            StaffID = .Cells(R, NwsStaff).Value
            n = R - NwsFirstDataRow
            Spike(n) = StaffID
            For i = 1 To UBound(Arr)
                If Arr(i, 1) >= StaffID Then
                    Spike(n) = Spike(n) & "," & .Cells(i + 1, NwsStaff).Value
                End If
            Next i
        Next R

        .Columns(NwsManager).Resize(, 2).ClearContents
        .Cells(1, NwsManager).Value = "Manager"
        .Cells(1, NwsUserID).Value = "UserID"
        R = NwsFirstDataRow

        For n = 0 To UBound(Spike)
            Sp = Split(Spike(n), ",")

            For i = 0 To UBound(Sp)
                If Len(Sp(i)) = 0 Then Exit For

                If Sp(i) <> Sp(0) Then
                    .Cells(R, NwsManager).Value = Sp(0)
                    .Cells(R, NwsUserID).Value = Sp(i)
                    R = R + 1
                End If
            Next i
        Next n

        Rl = .Cells(.Rows.Count, NwsManager).End(xlUp).Row
        Set Rng = .Range(.Cells(NwsFirstDataRow, NwsManager), _
                         .Cells(Rl, NwsUserID))
    End With
    SortData Rng

    Application.ScreenUpdating = True
End Sub

Private Sub SortData(Rng As Range, _
                     Optional ByVal SortColumn As Nws)

    With Rng.Worksheet.Sort
        With .SortFields
            .Clear
            If (SortColumn = NwsStaff) Or (SortColumn = 0) Then
                .Add Key:=Rng.Columns(NwsStaff), SortOn:=xlSortOnValues, _
                     Order:=xlAscending, DataOption:=xlSortNormal
            End If
            If (SortColumn = NwsSuper) Or (SortColumn = 0) Then
                .Add Key:=Rng.Columns(NwsSuper), SortOn:=xlSortOnValues, _
                     Order:=xlAscending, DataOption:=xlSortNormal
            End If
        End With
        .SetRange Rng
        .Header = xlNo
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End Sub

此代码将在名为“ Sheet1”的工作表上运行(在过程SortHierarchy中进行修改),并且需要A和B列中的数据(在代码顶部的Enum中进行修改。运行过程{{1 }}。

我在A和B列中的数据可能与您的数据不同。为清楚起见,我将列称为“ StaffID”和“ ReportsTo”。原则上,每个员工都向某人报告。因此,A列可能包含所有工作人员的列表。应用的逻辑是,如果工人向主管报告,主管向经理报告,经理向老板报告,那么工人,主管和经理都向老板报告。老板不向任何人报告,也就是说他自己。可以向任何工作人员分配相同的身份。也可以让工人直接向老板汇报。所有这些都在“ StaffID”和“ ReportsTo”两列中表示。

宏将这些数据收集并分类到一个层次列表中,该列表被写入同一工作表上的两个空白列中。您可以在Enum中分配列。请注意,该宏将允许在A和B列之间的数据(可自由分配),并且这些数据将与正在排序的数据保持一致。