1

我有一个 SQL 优化问题的工程实践,我认为这是一个典型的案例,并且会帮助很多人。

SQL 服务器 2005,

首先,创建主表。这是一个人员信息表。

CREATE TABLE [dbo].[OLAPAgentDim](
    [RoleID] [varchar](50) NULL CONSTRAINT [DF_OLAPAgentDim_RoleID]  DEFAULT ((1)),
    [OLAPKey] [bigint] IDENTITY(1,1) NOT NULL,
    [FatherKey] [bigint] NULL,
    [FatherKeyValue] [nvarchar](100) NULL,
    [System] [varchar](6) NULL,
    [Level] [int] NULL,
    [IfLeaf] [real] NULL,
    [IfDel] [real] NULL CONSTRAINT [DF_OLAPAgentDim_IfDel]  DEFAULT ((0)),
    [SourceKey] [varchar](50) NULL,
    [MainDemoName] [nvarchar](100) NULL,
    [FastCode] [varchar](50) NULL,
    [TagValue] [varchar](50) NULL,
    [Script] [nvarchar](max) NULL,
    [Birthday] [datetime] NULL,
    [EarlyStartTime] [datetime] NULL,
    [StartTime] [datetime] NULL,
    [EndTime] [datetime] NULL,
    [EditTime] [datetime] NULL,
    [BecomesTime] [datetime] NULL,
    [ContractTime] [datetime] NULL,
    [ContractEndTime] [datetime] NULL,
    [XMLIcon] [nvarchar](max) NULL,
    [PassKey] [varchar](50) NULL CONSTRAINT [DF_OLAPAgentDim_PassKey]  DEFAULT ('N3pkY3RHaeZXA9mGJdfm8A=='),
    [Address] [nvarchar](100) NULL,
    [HomeTel] [varchar](50) NULL,
    [Mobile] [varchar](50) NULL,
    [Email] [varchar](100) NULL,
    [IDCard] [varchar](50) NULL,
    [IDSecu] [varchar](50) NULL,
    [IDEndowment] [varchar](50) NULL,
    [IDAccumulation] [varchar](50) NULL,
    [ContactPerson] [nvarchar](100) NULL,
    [ContactPersonTel] [varchar](50) NULL,
    [Others1] [varchar](50) NULL,
    [SexKey] [varchar](2) NULL CONSTRAINT [DF_OLAPAgentDim_SexKey]  DEFAULT ((1)),
    [SexKeyValue] [nvarchar](100) NULL,
    [MarrageKey] [varchar](2) NULL CONSTRAINT [DF_OLAPAgentDim_MarrageKey]  DEFAULT ((1)),
    [MarrageKeyValue] [nvarchar](100) NULL,
    [Nation] [nvarchar](50) NULL,
    [Race] [nvarchar](50) NULL,
    [PartyMemberKey] [varchar](2) NULL CONSTRAINT [DF_OLAPAgentDim_PartyMemberKey]  DEFAULT ((1)),
    [PartyMemberKeyValue] [nvarchar](100) NULL,
    [RegionKey] [bigint] NULL CONSTRAINT [DF_OLAPAgentDim_RegionKey]  DEFAULT ((1)),
    [RegionKeyValue] [nvarchar](100) NULL,
    [LeaveResonKey] [bigint] NULL CONSTRAINT [DF_OLAPAgentDim_LeaveResonKey]  DEFAULT ((1)),
    [LeaveResonKeyValue] [nvarchar](100) NULL,
    [RoleStr] [varchar](max) NULL,
    [RoleStrValue] [nvarchar](max) NULL,
    [LeaderKey] [bigint] NULL CONSTRAINT [DF_OLAPAgentDim_LeaderKey]  DEFAULT ((1)),
    [LeaderKeyValue] [nvarchar](100) NULL,
    [FastCode2] [varchar](50) NULL,
    [FastCode3] [varchar](50) NULL,
    [FastCode4] [varchar](50) NULL,
    [FastCode5] [varchar](50) NULL,
    [OtherAddress] [nvarchar](100) NULL,
    [ShowOrder] [int] NULL,
    [RaceKey] [bigint] NULL DEFAULT ((1)),
    [RaceKeyValue] [nvarchar](100) NULL,
    [DepartLevelKey] [bigint] NULL DEFAULT ((1)),
    [DepartLevelKeyValue] [nvarchar](100) NULL,
    [forumname] [nvarchar](100) NULL,
    [IfCloseKey] [bigint] NULL DEFAULT ((1)),
    [IfCloseKeyValue] [nvarchar](100) NULL,
    [InsureStartTime] [datetime] NULL,
    [AccumulationStartTime] [datetime] NULL,
    [Rate] [varchar](50) NULL,
    [DirectLeaderKey] [bigint] NULL CONSTRAINT [DF_OLAPAgentDim_DirectLeaderKey]  DEFAULT ((1)),
    [DirectLeaderAttriKey] [bigint] NULL CONSTRAINT [DF_OLAPAgentDim_DirectLeaderAttriKey]  DEFAULT ((1)),
    [DirectLeaderKeyValue] [nvarchar](100) NULL,
    [DirectLeaderSourceKey] [varchar](50) NULL,
    [DirectLeaderPartName] [nvarchar](100) NULL,
    [DirectLeaderPositionName] [nvarchar](100) NULL,
    [NOTSync] [int] NULL,
    [FatherPath] [nvarchar](max) NULL,
    [SaleDiscount] [real] NULL,
 CONSTRAINT [PK_OLAPAgent Dim] PRIMARY KEY CLUSTERED 
(
    [OLAPKey] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

其次,在表中插入大约 10,000 条记录。我认为 10,000 条记录对于 SQL SERVER 来说并不是一个很大的数字。你可以看到这实际上是一个父亲和孩子的维度表。ifleaf=0 的记录表示人的部门结构节点,ifleaf=1 的记录表示人。您可以使用 FahterKey 列定义父子关系。例如:

OLAPKey IfLeaf FatherKey DepartLevelKey  MainDemoName 
  2        0      0           1          IBM Company
  3        0      2           2          Sales Depart    
  4        0      2           2          Service Depart
  5        0      3           3          Sales Team1  
  6        1      5          NULL        John Smith
  7        1      4          NULL        Mary
......

DepartLevelKey 列表示离开节点的级别。所以在这张表中,我们可以保存整个 HR 树信息。

第三,我们看到问题SQL:

create table #t
(
TableID int IDENTITY(1,1),
OLAPKey bigint,
MainDemoName nvarchar(max)
)

declare @t4 table
(
TableID int IDENTITY(1,1),
MainDemoName nvarchar(max),
OLAPKeystr varchar(100)
)

declare @agentkey bigint
set @agentkey ='2'

    --Part A
    --DepartLevelKey=2, to get @agentkey node's all level=2 department

    ;WITH Result AS(
    SELECT OLAPKey,DepartLevelKey,maindemoname FROM OLAPAgentDim WHERE OLAPKey =@agentkey
     UNION ALL 
     SELECT a.OLAPKey,a.DepartLevelKey,a.maindemoname FROM  OLAPAgentDim AS a,Result AS b WHERE a.FatherKey = b.OLAPKey
    )

     insert #t select OLAPKey,maindemoname from Result where DepartLevelKey=4

    --Part B
    ;with One as  
    (  
    select *,convert(varchar(50),OLAPKey) as Re from #t  
    )
    insert @t4 select maindemoname,stuff((select ','+Re from One where One.maindemoname=#t.maindemoname for xml path('')),1,1,'') as Two  
    from #t 
    group by maindemoname  
    drop table #t

上面的 SQL 分为 Part A 和 Part B。 Part A SQL 获取根节点下的所有子节点(并过滤掉那些属于指定 DepartLevelKey 的子节点)。例如,要获取销售部门的子部门中 level=3 的所有人员。

B 部分 SQL 将行更改为列,例如:

Change:
TableID  OLAPKey MainDemoName
  1        6     Sales Team1
  2        10    Sales Team1
  3        12    Sales Team1
to:
TableID  MainDemoName OLAPKeystr
  1      Sales Team1   6,10,12

这样我们就得到了每个目标部门的人,做进一步的处理(这里省略)。

问题:A部分很慢,大约需要5分钟。B部分也很慢。

我想知道如何根据表结构对其进行优化。

你的,伊万

4

1 回答 1

0

尝试:

(i)将此索引添加到OLAPAgentDim

create index IX_OLAPAgentDim_FatherKey on OLAPAgentDim (FatherKey) include (DepartLevelKey, MainDemoName)

( ii )从变为MainDemoName。这与 中的列定义匹配。#tnvarchar(max)nvarchar(100)OLAPAgentDim

(iii)在 A 部分和 B 部分之间,即在 A 部分之后和 B 部分之前,将此索引添加到#t

create clustered index IX on #t (MainDemoName)
于 2013-04-10T07:07:21.193 回答