为什么这个查询 运行 这么慢?
Why this query is running so slow?
此查询运行速度非常快(<100 毫秒):
SELECT TOP (10)
[Extent2].[CompanyId] AS [CompanyId]
,[Extent1].[Id] AS [Id]
,[Extent1].[Status] AS [Status]
FROM [dbo].[SplittedSms] AS [Extent1]
INNER JOIN [dbo].[Sms] AS [Extent2]
ON [Extent1].[SmsId] = [Extent2].[Id]
WHERE [Extent2].[CompanyId] = 4563
AND ([Extent1].[NotifiedToClient] IS NULL)
如果我只添加一个时间过滤器,它会花费太长时间(22 秒!):
SELECT TOP (10)
[Extent2].[CompanyId] AS [CompanyId]
,[Extent1].[Id] AS [Id]
,[Extent1].[Status] AS [Status]
FROM [dbo].[SplittedSms] AS [Extent1]
INNER JOIN [dbo].[Sms] AS [Extent2]
ON [Extent1].[SmsId] = [Extent2].[Id]
WHERE [Extent2].Time > '2015-04-10'
AND [Extent2].[CompanyId] = 4563
AND ([Extent1].[NotifiedToClient] IS NULL)
我尝试在 Sms
table 的 [Time]
列上添加索引,但优化器似乎没有使用该索引。尝试使用 With (index (Ix_Sms_Time))
;但令我惊讶的是,这需要更多时间(29 秒!)。
下面是实际的执行计划:
两个查询的执行计划相同。这里提到的表有 5M 到 8M 行(索引 < 1% 碎片化并且统计数据已更新)。我在 16 核 32GB 内存 Windows 2008 R2 机器上使用 MS SQL Server 2008R2)
仅在客户端过滤器具有 运行 后才强制启动时间过滤器是否有帮助?
FI 就像这个例子:
;WITH ClientData AS (
SELECT
[E2].[CompanyId]
,[E2].[Time]
,[E1].[Id]
,[E1].[Status]
FROM [dbo].[SplittedSms] AS [E1]
INNER JOIN [dbo].[Sms] AS [E2]
ON [E1].[SmsId] = [E2].[Id]
WHERE [E2].[CompanyId] = 4563
AND ([E1].[NotifiedToClient] IS NULL)
)
SELECT TOP 10
[CompanyId]
,[Id]
,[Status]
FROM ClientData
WHERE [Time] > '2015-04-10'
使用以下 Index Key Columns
在 Sms
上创建索引(按此顺序):
- 公司编号
- 时间
您可能需要也可能不需要添加 Id
作为 Included Column
。
您的时间列是什么数据类型?
如果是日期时间,请尝试将您的“2015-04-10”转换为等效数据类型,以便它可以使用索引。
Declare @test datetime
Set @test='2015-04-10'
然后修改你的条件:
[Extent2].Time > @test
如果数据类型不匹配,sql 服务器会隐式转换为匹配的数据类型。并且任何函数或强制转换操作都会阻止使用索引。
我和@JonTirjan 在同一条轨道上,只有 Time 的索引会导致很多关键查找,所以你至少应该尝试以下操作:
create index xxx on Sms (Time, CompanyId) include (Id)
或
create index xxx on Sms (CompanyId, Time) include (Id)
如果 Id 是您的聚簇索引,则在 include 子句中不需要它。如果您的数据的很大一部分属于 CompanyID 4563,那么也可以将其作为包含列。
您在实际计划中看到的百分比只是基于行数假设的估计值,因此这些百分比有时是完全错误的。查看实际的行数/执行数 + 统计 IO 输出应该可以让您了解实际发生的情况。
想到两件事:
- 通过添加额外的限制,数据库将 'harder' 找到符合您的限制的前 10 个项目。从假设 10.000 项(总共 100 万)中查找前 10 行比从 100 项(总共 100 万)中查找前 10 行更容易。
- 索引未被使用可能是因为索引是在日期时间列上创建的,如果您也在其中存储时间,则效率不高。您可能想在 [time] 列上创建一个聚集索引(但是您必须删除现在在 [CompanyId] 列上的聚集索引,或者您可以创建一个计算列来存储 [time] 的日期部分time] 列,在此计算列上创建索引并在此列上进行筛选。
我发现 SplittedSms
table 上的外键列 (SmsId
) 没有索引。我做了一个,现在看来第二个查询几乎和第一个查询一样快。
现在的执行计划:
感谢大家的付出。
此查询运行速度非常快(<100 毫秒):
SELECT TOP (10)
[Extent2].[CompanyId] AS [CompanyId]
,[Extent1].[Id] AS [Id]
,[Extent1].[Status] AS [Status]
FROM [dbo].[SplittedSms] AS [Extent1]
INNER JOIN [dbo].[Sms] AS [Extent2]
ON [Extent1].[SmsId] = [Extent2].[Id]
WHERE [Extent2].[CompanyId] = 4563
AND ([Extent1].[NotifiedToClient] IS NULL)
如果我只添加一个时间过滤器,它会花费太长时间(22 秒!):
SELECT TOP (10)
[Extent2].[CompanyId] AS [CompanyId]
,[Extent1].[Id] AS [Id]
,[Extent1].[Status] AS [Status]
FROM [dbo].[SplittedSms] AS [Extent1]
INNER JOIN [dbo].[Sms] AS [Extent2]
ON [Extent1].[SmsId] = [Extent2].[Id]
WHERE [Extent2].Time > '2015-04-10'
AND [Extent2].[CompanyId] = 4563
AND ([Extent1].[NotifiedToClient] IS NULL)
我尝试在 Sms
table 的 [Time]
列上添加索引,但优化器似乎没有使用该索引。尝试使用 With (index (Ix_Sms_Time))
;但令我惊讶的是,这需要更多时间(29 秒!)。
下面是实际的执行计划:
两个查询的执行计划相同。这里提到的表有 5M 到 8M 行(索引 < 1% 碎片化并且统计数据已更新)。我在 16 核 32GB 内存 Windows 2008 R2 机器上使用 MS SQL Server 2008R2)
仅在客户端过滤器具有 运行 后才强制启动时间过滤器是否有帮助?
FI 就像这个例子:
;WITH ClientData AS (
SELECT
[E2].[CompanyId]
,[E2].[Time]
,[E1].[Id]
,[E1].[Status]
FROM [dbo].[SplittedSms] AS [E1]
INNER JOIN [dbo].[Sms] AS [E2]
ON [E1].[SmsId] = [E2].[Id]
WHERE [E2].[CompanyId] = 4563
AND ([E1].[NotifiedToClient] IS NULL)
)
SELECT TOP 10
[CompanyId]
,[Id]
,[Status]
FROM ClientData
WHERE [Time] > '2015-04-10'
使用以下 Index Key Columns
在 Sms
上创建索引(按此顺序):
- 公司编号
- 时间
您可能需要也可能不需要添加 Id
作为 Included Column
。
您的时间列是什么数据类型? 如果是日期时间,请尝试将您的“2015-04-10”转换为等效数据类型,以便它可以使用索引。
Declare @test datetime
Set @test='2015-04-10'
然后修改你的条件:
[Extent2].Time > @test
如果数据类型不匹配,sql 服务器会隐式转换为匹配的数据类型。并且任何函数或强制转换操作都会阻止使用索引。
我和@JonTirjan 在同一条轨道上,只有 Time 的索引会导致很多关键查找,所以你至少应该尝试以下操作:
create index xxx on Sms (Time, CompanyId) include (Id)
或
create index xxx on Sms (CompanyId, Time) include (Id)
如果 Id 是您的聚簇索引,则在 include 子句中不需要它。如果您的数据的很大一部分属于 CompanyID 4563,那么也可以将其作为包含列。
您在实际计划中看到的百分比只是基于行数假设的估计值,因此这些百分比有时是完全错误的。查看实际的行数/执行数 + 统计 IO 输出应该可以让您了解实际发生的情况。
想到两件事:
- 通过添加额外的限制,数据库将 'harder' 找到符合您的限制的前 10 个项目。从假设 10.000 项(总共 100 万)中查找前 10 行比从 100 项(总共 100 万)中查找前 10 行更容易。
- 索引未被使用可能是因为索引是在日期时间列上创建的,如果您也在其中存储时间,则效率不高。您可能想在 [time] 列上创建一个聚集索引(但是您必须删除现在在 [CompanyId] 列上的聚集索引,或者您可以创建一个计算列来存储 [time] 的日期部分time] 列,在此计算列上创建索引并在此列上进行筛选。
我发现 SplittedSms
table 上的外键列 (SmsId
) 没有索引。我做了一个,现在看来第二个查询几乎和第一个查询一样快。
现在的执行计划:
感谢大家的付出。