树数据到SQL Server中的平面数据

时间:2017-07-23 11:11:05

标签: sql-server sql-server-2012

我面向平面视图的问题树视图,我有一些来自树的数据,这些数据存储在左下方的图片中。我说树的顶层是level1,第二层是level2,依此类推。我的预期结果在右下图中表示。

enter image description here

如何在SQL Server中动态地将我的数据转换为我的预期结果?我们必须创建动态列level1,level2,level3等。你有什么主意吗?感谢。

以下是示例数据

IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL 
DROP TABLE #TestData;

CREATE TABLE #TestData (
    Id INT NOT NULL,
    SomeName VARCHAR(3) NOT NULL,
    ParentId INT NULL 
    );
INSERT #TestData (Id, SomeName, ParentId) VALUES 
    (1, 'O', NULL),
    (2, 'D1', 1),
    (3, 'D2', 1),
    (4, 'S1', 2),
    (5, 'S2', 2),
    (6, 'S1', 3),
    (7, 'SP1', 3);

SELECT * FROM #TestData td;

3 个答案:

答案 0 :(得分:2)

您可以查询如下:

;With Cte as (   --Recursive CTE for traversing tree
    Select *, convert(varchar(max),[name]) as NameLevel, 1 as Levl from #TreeData where ParentId is Null
    Union all
    Select t.Id, t.[Name], t.[ParentId], concat(c.NameLevel,',',t.[name]) as NameLevel, c.Levl + 1 as Levl  from Cte c
        inner join #TreeData t on c.Id = t.ParentId
)
Select * from ( 
    select c.Id, c.Levl, a.[value]
    ,RowN = row_number() over(partition by Id order by Levl) from cte c
    cross apply  string_split(c.NameLevel,',') a
) sq
pivot(max([value]) for RowN in([1],[2],[3])) p --Pivot for getting all data

输出如下:

+---+------+------+----+
| 1 |  2   |  3   | Id |
+---+------+------+----+
| O | NULL | NULL |  1 |
| O | D1   | NULL |  2 |
| O | D2   | NULL |  3 |
| O | D1   | S1   |  4 |
| O | D1   | S2   |  5 |
| O | D2   | S1   |  6 |
| O | D2   | SP1  |  7 |
+---+------+------+----+

在我使用的输入表和数据下面:

Create Table #TreeData(Id int, [name] varchar(10), ParentId int)

Insert into #TreeData(id, [name], ParentId) values
 (1,'O', null)
,(2,'D1',   1)
,(3,'D2',   1)
,(4,'S1',   2)
,(5,'S2',   2)
,(6,'S1',   3)
,(7,'SP1',  3)

答案 1 :(得分:1)

以下内容应该为您提供所要求的结果。

注意:此解决方案依赖于使用名为tfn_Tally的iTVF,我会在我的答案下面发布代码。

IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL 
DROP TABLE #TestData;

CREATE TABLE #TestData (
    Id INT NOT NULL,
    SomeName VARCHAR(3) NOT NULL,
    ParentId INT NULL 
    );
INSERT #TestData (Id, SomeName, ParentId) VALUES 
    (1, 'O', NULL),
    (2, 'D1', 1),
    (3, 'D2', 1),
    (4, 'S1', 2),
    (5, 'S2', 2),
    (6, 'S1', 3),
    (7, 'SP1', 3);

--  SELECT * FROM #TestData td;

--===========================================
--===========================================

IF OBJECT_ID('tempdb..#RecursionResults', 'U') IS NOT NULL 
DROP TABLE #RecursionResults;

WITH 
    cte_Recursion AS (
        SELECT 
            td.Id, 
            SomeName = CAST(CAST(td.SomeName AS BINARY(5)) AS VARBINARY(1000)),
            --td.ParentId,
            NodeLevel = 1
        FROM
            #TestData td
        WHERE 
            td.ParentId IS NULL
        UNION ALL
        SELECT 
            td.Id,
            SomeName = CAST(CONCAT(r.SomeName, CAST(td.SomeName AS BINARY(5))) AS VARBINARY(1000)),
            NodeLevel = r.NodeLevel + 1
        FROM
            cte_Recursion r
            JOIN #TestData td
                ON r.Id = td.ParentId
        )
SELECT 
    Id = ISNULL(r.Id, 0), r.SomeName, r.NodeLevel
    INTO #RecursionResults
FROM
    cte_Recursion r;

-- adding a clustered index Id eliminates the sort operation on final select.
ALTER TABLE #RecursionResults ADD PRIMARY KEY CLUSTERED (Id);

-----------------------------------------

DECLARE 
    @PivotCount INT = (SELECT MAX(rr.NodeLevel) FROM #RecursionResults rr),
    @PivotCols VARCHAR(1000) = '',
    @PivotCAV VARCHAR(8000) = '',
    @sql VARCHAR(8000),
    @Debug BIT = 0;     -- set to 0 to execute, 1 to DeBug.

SELECT TOP (@PivotCount)
    @PivotCols = CONCAT(@PivotCols, CHAR(13), CHAR(10), CHAR(9), 'L', t.n, '.Level_', t.n, ','),
    @PivotCAV = CONCAT(@PivotCAV, CHAR(13), CHAR(10), CHAR(9), 'CROSS APPLY ( VALUES (CAST(SUBSTRING(rr.SomeName,', (t.n - 1) * 5 + 1, ', ', 5, ') AS VARCHAR(5))) ) L', t.n, ' (Level_', t.n, ')'
        )
FROM 
    dbo.tfn_Tally(@PivotCount, 1) t;

SET @sql = CONCAT('
SELECT ', 
    STUFF(@PivotCols, 1, 1, ''), '
    rr.Id
FROM 
    #RecursionResults rr',
    @PivotCAV, '
ORDER BY 
    rr.Id;');

IF @Debug = 1
BEGIN
    PRINT(@sql);
END;
ELSE
BEGIN 
    EXEC (@sql);
END;

tfn_Tally的功能代码......

CREATE FUNCTION dbo.tfn_Tally
/* ============================================================================
07/20/2017 JL, Created. Capable of creating a sequense of rows 
                ranging from -10,000,000,000,000,000 to 10,000,000,000,000,000
============================================================================ */
(
    @NumOfRows BIGINT,
    @StartWith BIGINT 
)
RETURNS TABLE WITH SCHEMABINDING AS 
RETURN
    WITH 
        cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),   -- 10 rows
        cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),                             -- 100 rows
        cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b),                             -- 10,000 rows
        cte_n4 (n) AS (SELECT 1 FROM cte_n3 a CROSS JOIN cte_n3 b),                             -- 100,000,000 rows
        cte_Tally (n) AS (
            SELECT TOP (@NumOfRows)
                (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) + @StartWith
            FROM 
                cte_n4 a CROSS JOIN cte_n4 b                                                    -- 10,000,000,000,000,000 rows
            )
    SELECT 
        t.n
    FROM 
        cte_Tally t;
GO

HTH, 杰森

答案 2 :(得分:1)

\common\models\leave\table1::updateAllCounters(['id' => 1],$condition);