在SQL Server中动态展平层次结构

问题描述

我们有一个表LedgerAccount,其父子关系类似于:

CREATE TABLE [dbo].[LedgerAccounts](
[ledger_key] [int] NOT NULL,[Ledger] [nvarchar](12) NULL,[LedgerLevel] [int] NULL,[ParentAccount] [nvarchar](12) NULL,[LedgerDescription] [nvarchar](30) NULL,CONSTRAINT [PK_LedgerAccount] PRIMARY KEY CLUSTERED 
    (
[ledger_key] ASC
    )WITH (PAD_INDEX = OFF,STATISTICS_norECOmpuTE = OFF,IGnorE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]


    INSERT INTO [dbo].[LedgerAccounts]
    VALUES (40,'020000','020999','Participation'),(41,20,'021000','Participation in Group'),(42,'021999','Loans to..'),(43,'022000','Loans to group company'),(44,'022999','Participation in'),(45,'029999','Other Participation'),(46,30,'059999','Financial Fixed Assets'),(47,50,'TOT.BALANS','Fixed Assets'),(48,90,'TOT.GB','Total Balance sheet'),(49,99,'NULL','Total GL')

enter image description here

LedgerLevel定义层次结构中的级别。父级059999位于最高级别(在本示例中,即90),而0是最低级别的子节点。 我需要使用上表中的层次关系创建一个表/结构/tmp.table,如下所示:

enter image description here

在这里,我们可以参数化级别和级别ID的数量。 以下是我在不考虑参数化并且假设级别数= 4的情况下尝试的查询。 如何在不对级别数和级别ID进行硬编码的情况下实现相同目标? 我是sql的新手,并且具有sql的基本知识。

create or alter    view [dbo].[Ledgerview] as
WITH LedgerAccountstree AS
(
    SELECT 
        ledger_key,Ledger as CurrLedgerCode,Ledger,Ledger as Lvl0Code,LedgerDescription as Lvl0Description,cast('-' as nvarchar(12)) as Lvl1Code,cast('-' as nvarchar(30)) as Lvl1Description,cast('-' as nvarchar(12)) as Lvl2Code,cast('-' as nvarchar(30)) as Lvl2Description,cast('-' as nvarchar(12)) as Lvl3Code,cast('-' as nvarchar(30)) as Lvl3Description,ParentAccount,LedgerLevel
    FROM 
        [dbo].[LedgerAccounts]
    WHERE
        LedgerLevel = 50
    UNION ALL
    SELECT
        [dbo].[LedgerAccounts].ledger_key,LedgerAccountstree.CurrLedgerCode,[dbo].[LedgerAccounts].Ledger,LedgerAccountstree.Lvl0Code,LedgerAccountstree.Lvl0Description,case when [dbo].[LedgerAccounts].LedgerLevel = 30 then [dbo].[LedgerAccounts].Ledger else LedgerAccountstree.Lvl1Code end as Lvl1Code,case when [dbo].[LedgerAccounts].LedgerLevel = 30 then [dbo].[LedgerAccounts].LedgerDescription else LedgerAccountstree.Lvl1Description end as Lvl1Description,case when [dbo].[LedgerAccounts].LedgerLevel = 20 then [dbo].[LedgerAccounts].Ledger else LedgerAccountstree.Lvl2Code end as Lvl2Code,case when [dbo].[LedgerAccounts].LedgerLevel = 20 then [dbo].[LedgerAccounts].LedgerDescription else LedgerAccountstree.Lvl2Description end as Lvl2Description,case when [dbo].[LedgerAccounts].LedgerLevel = 0 then [dbo].[LedgerAccounts].Ledger else LedgerAccountstree.Lvl3Code end as Lvl3Code,case when [dbo].[LedgerAccounts].LedgerLevel = 0 then [dbo].[LedgerAccounts].LedgerDescription else LedgerAccountstree.Lvl3Description end as Lvl3Description,[dbo].[LedgerAccounts].ParentAccount,[dbo].[LedgerAccounts].LedgerLevel
    FROM 
        [dbo].[LedgerAccounts]
    JOIN
        LedgerAccountstree
        ON LedgerAccountstree.Ledger = [dbo].[LedgerAccounts].[ParentAccount]
)
SELECT
    ledger_key,Lvl0Code +'-'+ Lvl0Description as Level0,Lvl1Code +'-'+ Lvl1Description as Level1,Lvl2Code +'-'+ Lvl2Description as Level2,Lvl3Code +'-'+ Lvl3Description as Level3 
    
FROM 
       LedgerAccountstree
       

GO

解决方法

这应该可以满足您的需求。 这里有很多事情要做,并且试图将其分解为详尽的细节将很快成为TLDR。因此,如果您对我为什么要做某事或某事如何工作有特定疑问,请在评论中提出,然后我将更新答案以包含这些具体细节。

USE tempdb;
GO
IF OBJECT_ID('tempdb.dbo.LedgerAccounts','U') IS NOT NULL 
BEGIN DROP TABLE tempdb.dbo.LedgerAccounts; END;
GO

CREATE TABLE tempdb.dbo.LedgerAccounts (
    ledger_key int NOT NULL,Ledger nvarchar (12) NULL,LedgerLevel int NULL,ParentAccount nvarchar (12) NULL,LedgerDescription nvarchar (30) NULL,CONSTRAINT PK_LedgerAccount
        PRIMARY KEY CLUSTERED (ledger_key ASC)
        WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
GO

INSERT INTO tempdb.dbo.LedgerAccounts
VALUES (40,'020000','020999','Participation'),(41,20,'021000','Participation in Group'),(42,'021999','Loans to..'),(43,'022000','Loans to group company'),(44,'022999','Participation in'),(45,'029999','Other Participation'),(46,30,'059999','Financial Fixed Assets'),(47,50,'TOT.BALANS','Fixed Assets'),(48,90,'TOT.GB','Total Balance sheet'),(49,99,'NULL','Total GL');
GO

-- SELECT * FROM tempdb.dbo.LedgerAccounts la;

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

IF OBJECT_ID('tempdb..#build_path','U') IS NOT NULL 
BEGIN DROP TABLE #build_path; END;
GO

CREATE TABLE #build_path (
    ledger_key int NOT NULL,Ledger nvarchar(12) NOT NULL,ParentAccount nvarchar(30) NOT NULL,h_level int NOT NULL,h_path nvarchar(4000) NOT NULL 
    );
GO

WITH 
    cte_build_path AS (
        SELECT 
            la.ledger_key,la.Ledger,la.ParentAccount,h_level = 0,h_path = CONVERT(nvarchar(4000),RIGHT(REPLICATE(N' ',50) + la.ledger + N'-' + la.LedgerDescription,50))
        FROM
            dbo.LedgerAccounts la
        WHERE 
            la.Ledger LIKE '[0-9][0-9][0-9][0-9][0-9][0-9]'
            AND la.ParentAccount NOT LIKE '[0-9][0-9][0-9][0-9][0-9][0-9]'
        UNION ALL
        SELECT 
            la.ledger_key,h_level = bp.h_level + 1,bp.h_path + RIGHT(REPLICATE(N' ',50))
        FROM
            dbo.LedgerAccounts la
            JOIN cte_build_path bp
                ON la.ParentAccount = bp.Ledger
        )
INSERT #build_path (ledger_key,Ledger,ParentAccount,h_level,h_path)
SELECT
    bp  .ledger_key,bp.Ledger,bp.ParentAccount,bp.h_level,bp.h_path
FROM
    cte_build_path bp;
GO

-- SELECT * FROM #build_path bp

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

DECLARE 
    @d_col_count int = (SELECT MAX(bp.h_level) FROM #build_path bp) + 1,@d_sql nvarchar(MAX) = N'',@debug bit = 0;

SELECT TOP (@d_col_count)
    @d_sql = CONCAT(@d_sql,N',[level',x.rn,N'] = CASE WHEN bp.h_level >= ',N' THEN LTRIM(SUBSTRING(bp.h_path,',x.rn * 50 + 1,50)) ELSE N''---'' END'
    )
FROM
    (
        SELECT TOP (@d_col_count)
            rn = ROW_NUMBER() OVER (ORDER BY ac.object_id) - 1
        FROM
            sys.all_columns ac
        ) x
ORDER BY 
    x.rn ASC;

SELECT @d_sql = CONCAT(N'
SELECT
    bp.ledger_key,bp.Ledger',@d_sql,N'

FROM
    #build_path bp;');

IF @debug = 1 
BEGIN 
    PRINT(@d_sql);
END;
ELSE 
BEGIN
    EXEC sys.sp_executesql @d_sql
END;
GO

结果...

ledger_key  Ledger       level0                                             level1                                             level2                                             level3                                             level4                                             level5                                             level6                                             level7
----------- ------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------------------------------------------
47          059999       059999-Fixed Assets                                ---                                                ---                                                ---                                                ---                                                ---                                                ---                                                ---
46          029999       059999-Fixed Assets                                029999-Financial Fixed Assets                      ---                                                ---                                                ---                                                ---                                                ---                                                ---
45          022999       059999-Fixed Assets                                029999-Financial Fixed Assets                      022999-Other Participation                         ---                                                ---                                                ---                                                ---                                                ---
44          022000       059999-Fixed Assets                                029999-Financial Fixed Assets                      022999-Other Participation                         022000-Participation in                            ---                                                ---                                                ---                                                ---
43          021999       059999-Fixed Assets                                029999-Financial Fixed Assets                      022999-Other Participation                         022000-Participation in                            021999-Loans to group company                      ---                                                ---                                                ---
42          021000       059999-Fixed Assets                                029999-Financial Fixed Assets                      022999-Other Participation                         022000-Participation in                            021999-Loans to group company                      021000-Loans to..                                  ---                                                ---
41          020999       059999-Fixed Assets                                029999-Financial Fixed Assets                      022999-Other Participation                         022000-Participation in                            021999-Loans to group company                      021000-Loans to..                                  020999-Participation in Group                      ---
40          020000       059999-Fixed Assets                                029999-Financial Fixed Assets                      022999-Other Participation                         022000-Participation in                            021999-Loans to group company                      021000-Loans to..                                  020999-Participation in Group                      020000-Participation