【发布时间】:2011-09-14 02:05:06
【问题描述】:
其中一个查询(如下所示)需要 90 多秒才能执行。它从一个相当大的表 LogMessage 中返回约 500 行。如果从查询中删除ESCAPE N'~',它将在几秒钟内执行。同样,如果TOP (1000) 被删除,它会在几秒钟内执行。查询计划在第一种情况下显示Key Lookup (Clustered) PK_LogMessage, Index Scan (NonClustered) IX_LogMessage and Nested Loops (Inner Join)。删除子句 ESCAPE N'~' 或 TOP (1000) 后,查询计划会更改并显示 Clustered Index Scan (Clustered) PK_LogMessage。虽然我们正在考虑添加更多索引(可能在 ApplicationName 上),但我们想了解当前的情况。
查询是从Entity Framework 生成的,以防您想知道为什么要这样写。此外,实际查询更复杂,但这是表现出相同行为的最短版本。
查询:
SELECT TOP (1000)
[Project1].[MessageID] AS [MessageID],
[Project1].[TimeGenerated] AS [TimeGenerated],
[Project1].[SystemName] AS [SystemName],
[Project1].[ApplicationName] AS [ApplicationName]
FROM
(
SELECT
[Project1].[MessageID] AS [MessageID],
[Project1].[TimeGenerated] AS [TimeGenerated],
[Project1].[SystemName] AS [SystemName],
[Project1].[ApplicationName] AS [ApplicationName]
FROM
(
SELECT
[Extent1].[MessageID] AS [MessageID],
[Extent1].[TimeGenerated] AS [TimeGenerated],
[Extent1].[SystemName] AS [SystemName],
[Extent1].[ApplicationName] AS [ApplicationName]
FROM
[dbo].[LogMessage] AS [Extent1]
INNER JOIN
[dbo].[LogMessageCategory] AS [Extent2]
ON
[Extent1].[CategoryID] = [Extent2].[CategoryID]
WHERE
([Extent1].[ApplicationName] LIKE N'%tier%' ESCAPE N'~')
) AS [Project1]
) AS [Project1]
ORDER BY
[Project1].[TimeGenerated] DESC
表日志消息:
CREATE TABLE [dbo].[LogMessage](
[MessageID] [int] IDENTITY(1000001,1) NOT NULL,
[TimeGenerated] [datetime] NOT NULL,
[SystemName] [nvarchar](256) NOT NULL,
[ApplicationName] [nvarchar](512) NOT NULL,
[CategoryID] [int] NOT NULL,
CONSTRAINT [PK_LogMessage] PRIMARY KEY CLUSTERED
(
[MessageID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[LogMessage] WITH CHECK ADD CONSTRAINT [FK_LogMessage_LogMessageCategory] FOREIGN KEY([CategoryID])
REFERENCES [dbo].[LogMessageCategory] ([CategoryID])
ALTER TABLE [dbo].[LogMessage] CHECK CONSTRAINT [FK_LogMessage_LogMessageCategory]
ALTER TABLE [dbo].[LogMessage] ADD DEFAULT ((100)) FOR [CategoryID]
CREATE NONCLUSTERED INDEX [IX_LogMessage] ON [dbo].[LogMessage]
(
[TimeGenerated] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
表LogMessageCategory:
CREATE TABLE [dbo].[LogMessageCategory](
[CategoryID] [int] NOT NULL,
[Name] [nvarchar](128) NOT NULL,
[Description] [nvarchar](256) NULL,
CONSTRAINT [PK_LogMessageCategory] PRIMARY KEY CLUSTERED
(
[CategoryID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
查询计划 1(需要 90 多秒)
查询计划 2(大约需要 3 秒)
【问题讨论】:
-
你能发布执行计划 1 和 2 的确切代码吗?该计划似乎与发布的查询不匹配。该查询与 LogMessageCategory 表有一个连接,但在执行计划中没有表示(应该是一个 INNER JOIN)。 LogMessage 表也没有 CategoryID。
-
我也对 ESCAPE 的使用感到困惑。通常,ESCAPE 用于使通配符成为搜索的一部分。例如,您可以使用 LIKE '%50~%%' ESCAPE '~' 来搜索所有包含“50%”的字符串。但是您的 LIKE 语句不包含 '~'
-
@8kb,这是SQLMS中显示的查询计划。
-
@amit_g:你确定吗?例如,查询计划 1 扫描非聚集索引 IX_LogMessage,然后针对 LogMessage 中的聚集索引进行书签查找。那是执行计划中的内连接。但是您发布的查询也加入了 LogMessageCategory 表。此联接未在执行计划中表示。我在尝试在我的系统上重现查询时注意到了这一点。如果您将 INNER JOIN 取出到 LogMessageCategory,则查询计划与您所拥有的相匹配。
-
@David,我想了解这里发生了什么。此查询由 EF 生成,比我发布的查询复杂得多,因此我们无能为力。真正的问题是它为什么会发生。解决方法不是问题。我们会将查询移动到存储过程并调用它,而不是让 EF 生成它。
标签: sql sql-server performance sql-server-2008-r2 sql-execution-plan