阅读巨大的表上的性能调整

时间:2011-03-28 16:21:18

标签: performance sql-server-2008 indexing

我有一个包含超过一亿行的巨大表格,我必须查询此表格,以便在最短的时间内返回一组数据。

所以我用这个表定义创建了一个测试环境:

CREATE TABLE [dbo].[Test](
[Dim1ID] [nvarchar](20) NOT NULL,
[Dim2ID] [nvarchar](20) NOT NULL,
[Dim3ID] [nvarchar](4) NOT NULL,
[Dim4ID] [smalldatetime] NOT NULL,
[Dim5ID] [nvarchar](20) NOT NULL,
[Dim6ID] [nvarchar](4) NOT NULL,
[Dim7ID] [nvarchar](4) NOT NULL,
[Dim8ID] [nvarchar](4) NOT NULL,
[Dim9ID] [nvarchar](4) NOT NULL,
[Dim10ID] [nvarchar](4) NOT NULL,
[Dim11ID] [nvarchar](20) NOT NULL,
[Value] [decimal](21, 6) NOT NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
[Dim1ID] ASC,
[Dim2ID] ASC,
[Dim3ID] ASC,
[Dim4ID] ASC,
[Dim5ID] ASC,
[Dim6ID] ASC,
[Dim7ID] ASC,
[Dim8ID] ASC,
[Dim9ID] ASC,
[Dim10ID] ASC,
[Dim11ID] 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,000行用于测试目的。碎片目前为0.01%。

我想使用此查询从此表中读取一组行时提高性能:

DECLARE @Dim1ID nvarchar(20) = 'C1'
DECLARE @Dim9ID nvarchar(4) = 'VRT1'
DECLARE @Dim10ID nvarchar(4) = 'S1'
DECLARE @Dim6ID nvarchar(4) = 'FRA'
DECLARE @Dim7ID nvarchar(4) =  '' -- empty = all
DECLARE @Dim8ID nvarchar(4) = '' -- empty = all

DECLARE @Dim2 TABLE ( Dim2ID nvarchar(20) NOT NULL )
INSERT INTO @Dim2 VALUES ('A1'), ('A2'), ('A3'), ('A4');

DECLARE @Dim3 TABLE ( Dim3ID nvarchar(4) NOT NULL )
INSERT INTO @Dim3 VALUES ('P1');

DECLARE @Dim4ID TABLE ( Dim4ID smalldatetime NOT NULL )
INSERT INTO @Dim4ID VALUES ('2009-01-01'), ('2009-01-02'), ('2009-01-03');

DECLARE @Dim11 TABLE ( Dim11ID nvarchar(20) NOT NULL )
INSERT INTO @Dim11 VALUES ('Var0001'), ('Var0040'), ('Var0060'), ('Var0099')

SELECT   RD.Dim2ID,
         RD.Dim3ID,
         RD.Dim4ID,
         RD.Dim5ID,
         RD.Dim6ID,
         RD.Dim7ID,
         RD.Dim8ID,
         RD.Dim9ID,
         RD.Dim10ID,
         RD.Dim11ID,
         RD.Value
FROM     dbo.Test RD
         INNER JOIN @Dim2 R
           ON RD.Dim2ID = R.Dim2ID
         INNER JOIN @Dim3 C
           ON RD.Dim3ID = C.Dim3ID
         INNER JOIN @Dim4ID P
           ON RD.Dim4ID = P.Dim4ID
         INNER JOIN @Dim11 V
          ON RD.Dim11ID = V.Dim11ID
WHERE    RD.Dim1ID = @Dim1ID
         AND RD.Dim9ID = @Dim9ID
         AND ((@Dim6ID <> '' AND RD.Dim6ID = @Dim6ID) OR @Dim6ID = '')
         AND ((@Dim7ID <> '' AND RD.Dim7ID = @Dim7ID) OR @Dim7ID = '')
         AND ((@Dim8ID <>'' AND RD.Dim8ID = @Dim8ID) OR @Dim8ID = '')

我已经测试了这个查询,并且这些时间返回了180行: 第一次执行:1分32秒;第二次执行:1分钟

如果可能,我想在几秒钟内返回数据。

我想我可以添加非聚集索引,但我不确定设置非聚簇索引的最佳方法是什么! 如果在此表中排序的订单数据可以改善性能? 或者除了索引之外还有其他解决方案吗?

感谢。

4 个答案:

答案 0 :(得分:3)

将您的数据类型视为一个问题。 Do you need nvarchar?这是measurably slower

第二个问题:PK对您的查询是错误的,首先应该是Dim1ID, Dim9ID(反之亦然,基于选择性)。或者是JOIN列中的一些风味。

第三个问题:使用OR。这种结构通常是有效的,尽管那些没有尝试过的人会发帖。

RD.Dim7ID = ISNULL(@Dim7ID, RD.Dim7ID)

这假设@ Dim7ID是NULL。在大多数情况下,优化器会将其短路。

答案 1 :(得分:2)

我和gbn一起。通常在星型模式数据仓库中,维度ID为int,即4个字节。不仅所有的尺寸都大于那个,nvarchar都是变化的并且使用宽字符。

就索引而言,只有一个聚类索引可能没问题,因为在事实表的情况下,你真的没有很多事实。正如gbn所说,根据您的特定示例,您的索引需要按照您将要提供的列的顺序,以便实际可以使用索引。

在具有大量事实的事实表的实际案例中,您的聚簇索引仅用于数据组织 - 您可能期望某些非聚集索引用于特定用途。

但我担心您的查询指定了ID参数。通常在DW环境中,您不知道ID,对于选择性查询,您根据维度进行选择,并且ID是无意义的代理:

SELECT *
FROM fact
INNER JOIN dim1
    ON fact.dim1id = dim1.id
WHERE dim1.attribute = ''

你看过Kimball关于尺寸建模的书吗?我想如果你想要一个明星模式,你可能应该熟悉他的设计技巧,以及他讨论过的太多和太少维度的各种陷阱。

答案 2 :(得分:0)

请参阅:Dynamic Search Conditions in T-SQL Version for SQL 2008 (SP1 CU5 and later)

快速回答,如果您使用的是SQL Server 2008的正确服务包,那就是 尝试将其添加到查询的末尾:

OPTION(RECOMPILE) 

在SQL Server 2008的正确服务包上,OPTION(RECOMPILE)将根据局部变量的运行时值构建执行计划。

对于仍在使用SQl Server 2008且没有正确的服务包或仍在使用2005的用户,请参阅:Dynamic Search Conditions in T-SQLVersion for SQL 2005 and Earlier

答案 3 :(得分:0)

我有点担心你的聚集索引中包含所有非值列。这将在非叶级别中产生大的索引。并且,该密钥将用于非聚簇索引中。而且,只有在查询中包含[Dim1ID]时,它才会提供任何好处。因此,即使您只是优化此查询,您也可能会进行全面扫描。

我会考虑最常用密钥的聚簇索引,如果你有很多与日期相关的查询(例如,a和b之间的日期),请使用日期密钥。然后,在其他键值上创建非聚簇索引。