2013-04-10 109 views
1

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

SQL SERVER 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] 

其次,向表中插入10000条记录。我认为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 [email protected] 
    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上述分为A部分和B部分 部分A SQL得到根节点下的所有儿童(和过滤那些属于指定的DepartLevelKey)。例如,让销售部门的所有人员的级别= 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部分也很慢。

我不知道如何根据表结构存在优化它。

你的, 伊万

+3

1)什么是您的SQL计划说。 2)你为什么打扰临时表? – 2013-04-10 04:53:15

+1

您应该注意不要引起不必要的类型转换;如果你有'声明@agentkey bigint' - 那么***为什么***在地球上将你的值设置为**字符串**'set @agentkey ='2'' ?????这是一个数字 - 只需将其设置为数字值即可!'set @agentkey = 2'如果你的查询中有这样的*隐式类型转换,那么这些都是真正的性能杀手... – 2013-04-10 04:55:59

+0

联盟往往非常缓慢。你为什么不离开外连接? – 2013-04-10 05:52:52

回答

0

尝试:

(ⅰ)添加这个索引来OLAPAgentDim

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

(ⅱ)nvarchar(max)#t更改MainDemoNamenvarchar(100)。这与OLAPAgentDim中的列定义匹配。

(III) A部分和B部分之间,即,后部分和B部分之前,添加该指数#t

create clustered index IX on #t (MainDemoName)