按不同列过滤时,SQL Server查询速度不同

时间:2013-08-15 16:04:51

标签: sql sql-server sql-server-2008

我有一个只有一个表Logs的数据库,其中包含列:

  • Id(PK Clustered,int,not null),
  • ServiceName(nvarchar(255),非null)和其他一些列,如
  • TaskVariant(nvarchar(1024)),
  • Source(nvarchar(1024))。

我在INDEX_SERVICENAME列上创建了一个索引ServiceName(非唯一,非群集),其中包含除Id, ServiceName以外的所有列。

  • 数据库大小约为4 GB。
  • 表包含约3 500 000行。
  • 表包含大约1 400 000行,Source = N'IpJob'。
  • 表包含大约2 400 000行,TaskVariant = N'Ip'。
  • 表包含大约600,000行,ServiceName ='1',TaskVariant = N'Ip'。
  • 表包含大约350 000行,ServiceName ='1',Source = N'IpJob'。

问题:

我希望通过ServiceNameTaskVariantSource选择表格过滤中的所有列。我的原始查询用于选择按Source过滤的最后100个项目是:

SELECT TOP (100) 
[Filter1].[Id] AS [Id], 
[Filter1].[Date] AS [Date], 
[Filter1].[Data] AS [Data], 
[Filter1].[ServiceName] AS [ServiceName], 
[Filter1].[LogLevel] AS [LogLevel], 
[Filter1].[StackTrace] AS [StackTrace], 
[Filter1].[TaskVariant] AS [TaskVariant], 
[Filter1].[Source] AS [Source], 
[Filter1].[Message] AS [Message]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[Date] AS [Date], [Extent1].[Data] AS [Data], [Extent1].[ServiceName] AS [ServiceName], [Extent1].[LogLevel] AS [LogLevel], [Extent1].[StackTrace] AS [StackTrace], [Extent1].[TaskVariant] AS [TaskVariant], [Extent1].[Source] AS [Source], [Extent1].[Message] AS [Message], row_number() OVER (ORDER BY [Extent1].[Id] DESC) AS [row_number]
    FROM [dbo].[Logs] AS [Extent1]
    WHERE (@serviceName = [Extent1].[ServiceName]) AND (@source = [Extent1].[Source])
)  AS [Filter1]
WHERE [Filter1].[row_number] > 0
ORDER BY [Filter1].[Id] DESC

此查询的工作时间非常快〜00:00:00。

但是当我尝试按TaskVariant过滤时,查询需要~00:02:18分钟(下一个查询)。

SELECT TOP (100) 
[Filter1].[Id] AS [Id], 
[Filter1].[Date] AS [Date], 
[Filter1].[Data] AS [Data], 
[Filter1].[ServiceName] AS [ServiceName], 
[Filter1].[LogLevel] AS [LogLevel], 
[Filter1].[StackTrace] AS [StackTrace], 
[Filter1].[TaskVariant] AS [TaskVariant], 
[Filter1].[Source] AS [Source], 
[Filter1].[Message] AS [Message]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[Date] AS [Date], [Extent1].[Data] AS [Data], [Extent1].[ServiceName] AS [ServiceName], [Extent1].[LogLevel] AS [LogLevel], [Extent1].[StackTrace] AS [StackTrace], [Extent1].[TaskVariant] AS [TaskVariant], [Extent1].[Source] AS [Source], [Extent1].[Message] AS [Message], row_number() OVER (ORDER BY [Extent1].[Id] DESC) AS [row_number]
    FROM [dbo].[Logs] AS [Extent1]
    WHERE (@serviceName = [Extent1].[ServiceName]) AND (@taskVariant = [Extent1].[TaskVariant])
)  AS [Filter1]
WHERE [Filter1].[row_number] > 0
ORDER BY [Filter1].[Id] DESC

问题:为什么第二个查询的执行速度要慢得多,以及如何解决这个问题?

非常感谢你的建议。

执行计划1

2 个答案:

答案 0 :(得分:1)

索引的工作方式类似于层次结构/树,其级别与其中的列相对应。

因此,如果您的索引位于ServiceName, TaskVariant,则可以快速过滤到特定的ServiceName,因为这是树中的顶级。

但是,如果您尝试按TaskVariant进行过滤,则现在必须仔细阅读整个索引:您不能只跳转到特定的TaskVariant,因为相同的TaskVariant将是在不同的ServiceName s。

如果要对TaskVariant进行过滤,则需要以TaskVariant开头的另一个索引。注意:不要只为每一列创建完整的索引:每个索引占用额外的空间,需要在UPDATEINSERT上进行更多的工作

答案 1 :(得分:0)

您看到的执行时间的差异主要是由于第一个具有索引而第二个没有。至于为什么它有这么大的差异,可能是因为有一个索引,这意味着值被排序。

由于值已经过排序,因此您可以使用非常有效的字符串搜索算法,这些算法可以在过滤数量级时减少操作次数。

此外,还有许多其他特征会影响这一点。整个索引可能在内存中,而表数据则不是,因此第一个查询中的过滤可以在内存中完成,而不会触及磁盘,而另一个可能没有。