表的非规范化(Pivot?)

时间:2016-01-08 17:17:39

标签: sql sql-server

我正面临这个问题,我不知道如何解决。

我将组织结构存储在2个表,组织和关系中;我的目标是构建(可以通过存储过程或类似的东西) DENORMALIZED 表,报告每个组织单位一行,通往顶部的路径树,有一个固定的深度。

我实际上可以使用游标和其他类似的东西来做到这一点,但我想知道是否有更快的方法(游标需要超过4分钟处理~4500个对象代码)

organizations表有四个字段:

  1. 目标代码
  2. 对象描述
  3. 开始对象的日期
  4. 对象的结束日期(如果无限,则为31 dec 9999)
  5. relations表有四个字段:

    1. 目标代码
    2. 父亲代码
    3. 开始关系日期
    4. 关系的结束日期(如果无限,则为31 dec 9999)
    5. 结果表应包含以下字段:

      1. obj code
      2. obj description
      3. 根对象代码(lev 0)
      4. root object descr(lev 0)
      5. 第一个祖先代码(lev 1)
      6. 第一个对象descr(lev 1)
      7. 第二个目标代码(第2版)
      8. 第二个对象descr(lev 2)
      9. 依此类推,直到12级 - 固定
      10. 现在,我可以使用以下SQL

        获取所有记录
        DECLARE
            @datRif dateTime = convert(datetime, '20151231', 103),
            @codUo char(8) = '50043899',
            @withMe char(1) = ' ',
            @currentUo char(8) = '50043899'
        
        DECLARE @path TABLE (
            leaf char(8),
            ancestor char(8),
            desAncestor varchar(50),
            deepLevel int
        )
        
        DECLARE cUOs cursor for
            SELECT objCode
                FROM organizations
            WHERE 
                objType = 'O' AND
                begDate <= @datRif AND
                endDate >= @datRif
        
        OPEN cUOs
        
        FETCH NEXT FROM cUOs 
        INTO @currentUo
        
        WHILE @@FETCH_STATUS = 0
        BEGIN
        
        
            with n (codUo, deepLev) AS
            (SELECT r.objCode, 0
                FROM relations r 
                WHERE r.objCode = @currentUo and 
                        r.begDate <= @datRif and r.endDate >= @datRif
             UNION ALL
             SELECT nplus1.fatherCode, n.deepLev + 1 
                FROM relations  nplus1, n
                WHERE n.codUo = nplus1.objCode   and 
                        nplus1.begDate <= @datRif and nplus1.endDate >= @datRif
            )
        
            insert into @path
            select @currentUo as leaf, n.codUo, o.longText, n.deepLev
            from n left outer join organizations o on n.codUo = o.objCode
            WHERE (o.begDate <= @datRif and o.endDate >= @datRif) or 
                o.begDate = null
            order by n.deepLev DESC, o.longText
        
        -- Get the next UO code.
        FETCH NEXT FROM cUOs 
            INTO @currentUo;
        
        END
        CLOSE cUOs
        DEALLOCATE cUOs
        
        SELECT *
        from @path
        

        这段代码给了我以下数据集:

        leaf        ancestor    desAncestor     deepLevel 
        50000135    50032466    HOLDING         1
        50000135    50000135    CEO             0 
        50023726    50032466    HOLDING         2
        50023726    50000135    CEO             1 
        50023726    50023726    CEO_HR          0
        50016541    50032466    HOLDING         3 
        50016541    50000135    CEO             2
        50016541    50023726    CEO_HR          1 
        50016541    50016541    CEO_HR_ORG      0
        50043899    50032466    HOLDING         4 
        50043899    50000135    CEO             3
        50043899    50023726    CEO_HR          2 
        50043899    50016541    CEO_HR_ORG      1
        50043899    50043899    CEO_HR_ORG_HRIS 0
        

        我真正需要的是这样的事情:

        object      object descr    root        root desc       uo1         uo1 desc        uo2         uo2 desc        uo3         uo3 desc        uo4         uo4 desc        
        50043899    CEO_HR_ORG_HRIS 50032466    HOLDING         50000135    CEO             50023726    CEO_HR          50016541    CEO_HR_ORG      
        50016541    CEO_HR_ORG      50032466    HOLDING         50000135    CEO             50023726    CEO_HR          
        50023726    CEO_HR          50032466    HOLDING         50000135    CEO             
        50000135    CEO             50032466    HOLDING         
        

        如果有人想看一下,我会添加一些脚本来生成表格和数据。 (正在运行的DB是MS-SQL 2008 r2)

        提前谢谢

        /****** Object:  Table [dbo].[organizations]    Script Date: 08/01/2016 17:44:27 ******/
        SET ANSI_NULLS ON
        GO
        
        SET QUOTED_IDENTIFIER ON
        GO
        
        SET ANSI_PADDING ON
        GO
        
        CREATE TABLE [dbo].[organizations](
            [objCode] [nvarchar](8) NOT NULL,
            [begDate] [datetime] NOT NULL,
            [endDate] [datetime] NOT NULL,
            [longText] [nvarchar](40) NULL,
        ) ON [PRIMARY]
        
        GO
        
        SET ANSI_PADDING OFF
        GO
        
        /****** Object:  Table [dbo].[relations]    Script Date: 08/01/2016 17:46:28 ******/
        SET ANSI_NULLS ON
        GO
        
        SET QUOTED_IDENTIFIER ON
        GO
        
        SET ANSI_PADDING ON
        GO
        
        CREATE TABLE [dbo].[relations](
            [fatherCode] [nvarchar](8) NOT NULL,
            [objCode] [nvarchar](8) NOT NULL,
            [begDate] [datetime] NOT NULL,
            [endDate] [datetime] NOT NULL
        ) ON [PRIMARY]
        
        GO
        
        SET ANSI_PADDING OFF
        GO
        
        
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50000135', CAST(0x00008C8700000000 AS DateTime), CAST(0x000095CE00000000 AS DateTime) N'CEO')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x000090F300000000 AS DateTime), CAST(0x0000919D00000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000919E00000000 AS DateTime), CAST(0x0000936F00000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000937000000000 AS DateTime), CAST(0x0000945600000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50023726', CAST(0x0000943E00000000 AS DateTime), CAST(0x0000945200000000 AS DateTime), N'CEO_HR')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50023726', CAST(0x0000945300000000 AS DateTime), CAST(0x0000945600000000 AS DateTime), N'CEO_HR')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000945700000000 AS DateTime), CAST(0x0000945700000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50023726', CAST(0x0000945700000000 AS DateTime), CAST(0x0000946000000000 AS DateTime), N'CEO_HR')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000945800000000 AS DateTime), CAST(0x0000946000000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000946100000000 AS DateTime), CAST(0x0000974A00000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50023726', CAST(0x0000946100000000 AS DateTime), CAST(0x000097B300000000 AS DateTime), N'CEO_HR')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50000135', CAST(0x000095CF00000000 AS DateTime), CAST(0x00009A1500000000 AS DateTime), N'CEO')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000974B00000000 AS DateTime), CAST(0x000097F000000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50032466', CAST(0x0000977700000000 AS DateTime), CAST(0x0000A0DC00000000 AS DateTime), N'HOLDING')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50023726', CAST(0x000097B400000000 AS DateTime), CAST(0x000098A800000000 AS DateTime), N'CEO_HR')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x000097F100000000 AS DateTime), CAST(0x0000A14900000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50023726', CAST(0x000098A900000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime), N'CEO_HR')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50000135', CAST(0x00009A1600000000 AS DateTime), CAST(0x00009CD200000000 AS DateTime), N'CEO')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50000135', CAST(0x00009CD300000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime), N'CEO')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50043899', CAST(0x00009EC200000000 AS DateTime), CAST(0x0000A13800000000 AS DateTime), N'CEO_HR_ORG_HRIS')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50032466', CAST(0x0000A0DD00000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime), N'HOLDING')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50043899', CAST(0x0000A13900000000 AS DateTime), CAST(0x0000A28600000000 AS DateTime), N'CEO_HR_ORG_HRIS')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50016541', CAST(0x0000A14A00000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime), N'CEO_HR_ORG')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50043899', CAST(0x0000A28700000000 AS DateTime), CAST(0x0000A3A700000000 AS DateTime), N'CEO_HR_ORG_HRIS')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50043899', CAST(0x0000A3A800000000 AS DateTime), CAST(0x0000A3A800000000 AS DateTime), N'CEO_HR_ORG_HRIS')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50043899', CAST(0x0000A3A900000000 AS DateTime), CAST(0x0000A45600000000 AS DateTime), N'CEO_HR_ORG_HRIS')
        INSERT [dbo].[organizations] ([objCode], [begDate], [endDate], [longText]) VALUES (N'50043899', CAST(0x0000A45700000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime), N'CEO_HR_ORG_HRIS')
        INSERT [dbo].[relations] ([fatherCode], [begDate], [endDate]) VALUES (N'50000135', N'50023726', CAST(0x0000943E00000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime))
        INSERT [dbo].[relations] ([fatherCode], [begDate], [endDate]) VALUES (N'50015420', N'50016541', CAST(0x000090F300000000 AS DateTime), CAST(0x0000945600000000 AS DateTime))
        INSERT [dbo].[relations] ([fatherCode], [begDate], [endDate]) VALUES (N'50016541', N'50043899', CAST(0x0000A14A00000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime))
        INSERT [dbo].[relations] ([fatherCode], [begDate], [endDate]) VALUES (N'50023726', N'50016541', CAST(0x0000945700000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime))
        INSERT [dbo].[relations] ([fatherCode], [begDate], [endDate]) VALUES (N'50032466', N'50000135', CAST(0x0000A0DD00000000 AS DateTime), CAST(0x002D247F00000000 AS DateTime))
        INSERT [dbo].[relations] ([fatherCode], [begDate], [endDate]) VALUES (N'50042187', N'50043899', CAST(0x00009EC200000000 AS DateTime), CAST(0x0000A14900000000 AS DateTime))
        

1 个答案:

答案 0 :(得分:1)

你可以试试这个......我不能说它会有更好的表现,但它会得到结果。

我首先在CTE上使用了所有(因此名称),但是你可能会在临时表中获得更好的性能,所以我使用了临时表。

您仍然需要添加6-12级。

DECLARE @datRif dateTime = convert(datetime, '20151231', 103)

SELECT  COALESCE(r.objCode,o.objCode) objCode,
        longText,
        r.fatherCode
INTO    #baseCTE
FROM    dbo.organizations o
        LEFT JOIN dbo.relations r ON o.objCode = r.objCode
                                        AND @datRif BETWEEN r.begDate AND r.endDate
WHERE   @datRif BETWEEN o.begDate AND o.endDate

;WITH recursiveCte AS 
(
    SELECT  objCode,
            longText,
            fatherCode,
            objCode AS [Root],
            0 AS [Level]
    FROM    #baseCTE
    UNION ALL 
    SELECT  base.objCode,
            base.longText,
            base.fatherCode,
            [Root],
            [Level] + 1
    FROM    #baseCTE base
            JOIN recursiveCte c ON c.fatherCode = base.objCode

)
SELECT  *, 
        COUNT(*) OVER (PARTITION BY [Root]) Cnt,
        ROW_NUMBER() OVER (PARTITION BY [Root] ORDER BY [Level] DESC) Rn
INTO    #groupedCte
FROM    recursiveCte 

SELECT  cte.longText,
        cte.[Root] objRoot,
        cte.objCode,
        [root].objCode AS [root],
        [root].longText AS [root desc],
        cte.Cnt, 
        cte.Level,
        ROW_NUMBER() OVER (PARTITION BY cte.Cnt ORDER BY cte.Rn) [order]
INTO    #finalCte
FROM    #groupedCte cte
        JOIN (SELECT * FROM #groupedCte WHERE RN = 1) [root] ON [root].[Root] = cte.[Root]
WHERE   cte.Rn <> 1

SELECT  t.objRoot [object],
        t.longText [object descr],
        t.root,
        t.[root desc],
        uo1.objCode [uo1],
        uo1.longText [uo1 desc],
        uo2.objCode [uo2],
        uo2.longText [uo2 desc],
        uo3.objCode [uo3],
        uo3.longText [uo3 desc],
        uo4.objCode [uo4],
        uo4.longText [uo4 desc],
        uo5.objCode [uo5],
        uo5.longText [uo5 desc]
FROM    #finalCte t
        OUTER APPLY(SELECT * FROM #finalCte f WHERE f.Cnt = t.Cnt AND f.[order] = 1 AND f.Level <> 0) uo1
        OUTER APPLY(SELECT * FROM #finalCte f WHERE f.Cnt = t.Cnt AND f.[order] = 2 AND f.Level <> 0) uo2
        OUTER APPLY(SELECT * FROM #finalCte f WHERE f.Cnt = t.Cnt AND f.[order] = 3 AND f.Level <> 0) uo3
        OUTER APPLY(SELECT * FROM #finalCte f WHERE f.Cnt = t.Cnt AND f.[order] = 4 AND f.Level <> 0) uo4
        OUTER APPLY(SELECT * FROM #finalCte f WHERE f.Cnt = t.Cnt AND f.[order] = 5 AND f.Level <> 0) uo5
WHERE   t.Level = 0
ORDER BY t.Cnt DESC,
        t.Level