2011-03-28 77 views
0

我有一个超过一亿行的巨大表格,我必须查询此表格才能在最短的时间内返回一组数据。在阅读巨大表格时的性能调整

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

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] 

此表是星型模式架构(事实上/尺寸)的事实表。正如你所看到的,除了“Value”列之外,我在所有列上都有一个聚集索引。

我已经用大约填充了这些数据。 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日执行: 1分32秒;第二次执行:1分钟

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

我想我可以添加非聚集索引,但我不知道最好的方法是设置非聚集索引! 如果在此表中排序了订单数据可以提高绩效? 还是有其他解决方案比索引?

谢谢。

+0

脑融化。什么是变量命名? – 2011-03-28 16:26:36

+0

为了保密,我宁愿不要输入真实姓名。但我想我可以写出虚拟名字。 – Dan 2011-03-28 16:45:38

回答

3

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

问题二:PK是错误的查询,它应该是Dim1ID, Dim9ID第一(或反之亦然基于选择性)。或一些味道与JOIN列中。

第三个问题:使用或。尽管没有尝试过的人会说话,但这个构造通常是有效的。

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

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

0

看到这一点:Dynamic Search Conditions in T-SQL Version for SQL 2008 (SP1 CU5 and later)

快速的解答,如果您是在SQL Server 2008中的正确的服务包,就是 尝试添加到查询的末尾:

OPTION(RECOMPILE) 

当SQL Server 2008的正确Service Pack,OPTION(RECOMPILE)将根据本地变量的运行时值构建执行计划。

对于人于2005年见犹使用SQL Server 2008没有正确的服务包或静止:Dynamic Search Conditions in T-SQLVersion for SQL 2005 and Earlier

0

我会有点关心你的聚集索引中的所有非价值列。这将在非叶级别上形成一个大的索引。而且,该密钥将用于非聚集索引。而且,只有当查询中包含[Dim1ID]时,它才会提供任何好处。所以,即使你只是优化这个查询,你可能会得到一个完整的扫描。

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

+0

我们在关键和十亿行+中有更多列的类似表格。对于OLAP类型的表,这是正常的。 – gbn 2011-03-28 16:44:24

2

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

就索引而言,只有一个聚类索引可能没有问题,因为在事实表的情况下,您确实没有太多事实。正如gbn所说的,在你的特定例子中,你的索引需要按照你将要提供的列的顺序,这样索引才能真正被使用。

在一个事实表中,有许多事实的实际情况,您的聚集索引仅仅是数据组织 - 您可能会期望一些非聚集索引的具体用法。

但我担心你的查询指定了一个ID参数。通常,在DW环境,你不知道的ID,选择性查询,请根据尺寸,和IDS是没有意义的替代品:

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

你看着金博尔对三维建模的书?我认为,如果你要选择一个明星模式,你应该熟悉他的设计技巧,以及他讨论的太多和太少的缺陷。