【问题标题】:Flattening of hierarchy in SQL Server Dynamically动态扁平化 SQL Server 中的层次结构
【发布时间】:2021-01-01 22:40:27
【问题描述】:

我们有一个表 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', 0, '020999', 'Participation'),
(41, '020999', 20, '021000', 'Participation in Group'),
(42, '021000', 0, '021999', 'Loans to..'),
(43, '021999', 20, '022000', 'Loans to group company'),
(44, '022000', 0, '022999', 'Participation in'),
(45, '022999', 20, '029999', 'Other Participation'),
(46, '029999', 30, '059999', 'Financial Fixed Assets'),
(47, '059999', 50, 'TOT.BALANS', 'Fixed Assets'),
(48, 'TOT.BALANS', 90, 'TOT.GB', 'Total Balance sheet'),
(49, 'TOT.GB', 99, 'NULL', 'Total GL')

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

在这里,我们可以参数化关卡数量和关卡ID。 以下是我在不考虑参数化并假设级别数 = 4 的情况下尝试过的查询。 如何在不硬编码级别数和级别 ID 的情况下实现相同的目标? 我是 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,
    Ledger,
    Lvl0Code +'-'+ Lvl0Description as Level0,
    Lvl1Code +'-'+ Lvl1Description as Level1,       
    Lvl2Code +'-'+ Lvl2Description as Level2,
    Lvl3Code +'-'+ Lvl3Description as Level3 
    
FROM 
       LedgerAccountstree
       

【问题讨论】:

  • 如果有动态数量的级别,您无法在 VIEW 中执行此操作。视图只是一个虚拟表,其定义由SELECT 语句定义;您不能包含变量和EXEC 命令之类的内容来执行动态 SQL。
  • 那么更好的选择是什么?正如我所说,我是这里的新手。
  • 如果您必须获得动态结果,则需要使用存储过程之类的东西,(显然)其行为与 VIEW 非常不同。

标签: sql-server dynamic parent-child hierarchy flatten


【解决方案1】:

这应该可以满足您的需求。 这里发生了很多事情,试图将其分解为微小的细节将很快成为 TLDR。因此,如果您对我为什么做某事或某事如何运作有具体问题,请在 cmets 中提问,我会更新答案以包含这些具体细节。

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', 0, '020999', 'Participation'),
    (41, '020999', 20, '021000', 'Participation in Group'),
    (42, '021000', 0, '021999', 'Loans to..'),
    (43, '021999', 20, '022000', 'Loans to group company'),
    (44, '022000', 0, '022999', 'Participation in'),
    (45, '022999', 20, '029999', 'Other Participation'),
    (46, '029999', 30, '059999', 'Financial Fixed Assets'),
    (47, '059999', 50, 'TOT.BALANS', 'Fixed Assets'),
    (48, 'TOT.BALANS', 90, 'TOT.GB', 'Total Balance sheet'),
    (49, 'TOT.GB', 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,
            la.Ledger,
            la.ParentAccount,
            h_level = bp.h_level + 1,
            h_path = CONVERT(nvarchar(4000), bp.h_path + RIGHT(REPLICATE(N' ', 50) + la.ledger + N'-' + la.LedgerDescription, 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 >= ', x.rn, N' THEN LTRIM(SUBSTRING(bp.h_path, ', x.rn * 50 + 1, N', 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    • 1970-01-01
    • 2016-06-03
    相关资源
    最近更新 更多