我有一个包含超过一亿行的巨大表格,我必须查询此表格,以便在最短的时间内返回一组数据。
所以我用这个表定义创建了一个测试环境:
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分钟。
如果可能,我想在几秒钟内返回数据。
我想我可以添加非聚集索引,但我不确定设置非聚簇索引的最佳方法是什么! 如果在此表中排序的订单数据可以改善性能? 或者除了索引之外还有其他解决方案吗?
感谢。
答案 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之间的日期),请使用日期密钥。然后,在其他键值上创建非聚簇索引。