Erw*_*ers 2 performance sql-server-2008 sql-server query-performance
我们有一个查询会在运行时破坏我们的生产服务器。
它是报告功能的一部分,不好的部分如下所示:
SELECT DISTINCT
mt.ID AS ID
FROM
[dbo].[MyTable] mt
WITH (NOLOCK)
WHERE
(@aVariable IS NULL
OR (CONVERT(VARCHAR(22), mt.Date1, 112) >= CONVERT(VARCHAR(22), @date1, 112))
AND (@status IS NULL
OR @status <> 2
OR ( @status = 2
AND ( SELECT COUNT(*)
FROM
MyTable mt2
WITH (NOLOCK)
WHERE
mt2.CaseID = mt.CaseID
AND mt2.Date1 > mt.Date1
) = 0
)
)
AND (@aSecondVariable IS NULL
OR (CONVERT(VARCHAR(22), mt.Date1, 112) <= CONVERT(VARCHAR(22), @date1, 112)))
AND (@aThirdVariable IS NULL
OR (CONVERT(VARCHAR(22), mt.Date2, 112) >= CONVERT(VARCHAR(22), @date2, 112)))
AND (@aFourtVariable IS NULL
OR (CONVERT(VARCHAR(22), mt.Date2, 112) <= CONVERT(VARCHAR(22), @date2, 112)))
Run Code Online (Sandbox Code Playgroud)
此外,表和索引的创建如下:
CREATE INDEX [MyIndex] ON [dbo].[MyTable]
([AColumn], [AColumn2], [Date1], [Date2])
WITH (FILLFACTOR = 90)
Run Code Online (Sandbox Code Playgroud)
MyTable有大约 80 列和一个单列 Primary Key: (ID)。
在MyTable mt由大约10.000.000行。有在包含该列的索引aVariable,aSecondVariable,aThirdVariable和aFourthVariable。日期列的大约一半值是空值。在索引中,它们位于第 3 和第 4 位。
当我们在一台服务器(没有用户)上运行查询时,它的表现非常好。当我们在生产中(与用户一起)运行它时,它花费的时间太长并且超时。
我们想知道这怎么可能。两台服务器上的执行计划相同。我们认为结果可能被缓存在某处或者空闲内存(2GB RAM)不足。
我们不是数据库性能方面的专家,希望有真正的 DBA 能给我们提供意见。谢谢。
ype*_*eᵀᴹ 14
几点建议:
DISTINCT作为ID是主键。您不可能在结果中得到重复的行。datetime列。这使您的条件不可 sargable,并且查询将始终进行表扫描。如果变量被声明为日期,则它们也不需要转换,但这对于可存储性来说不是问题。NOT EXISTS而不是(SELECT COUNT(*) ...) = 0检查是否没有匹配某些条件的行。WITH (NOLOCK)提示。另一篇博客:坏习惯:把NOLOCK无处不在和问题,在这个网站与一些宝贵的意见:是否NOLOCK总是不好?.(Date1)和(Date2)或索引(Date1, Date2)就可以了,但这需要测试。索引(CaseId, Date1)对子查询很有用。OPTION (RECOMPILE)(如@Mikael Eriksson 建议的那样)。这基本上告诉优化器不要依赖缓存计划,而是花一些时间为每个查询运行重新编译查询 - 即根据新参数值生成新计划。有 7 个变量可以显着改变查询,这似乎是一个非常好的选择。阅读来自@Paul White 的文章,了解“参数嵌入优化”、参数嗅探以及其他选项和优势的更详细说明:参数嗅探、嵌入和RECOMPILE选项。查询重写:
SELECT
mt.ID
FROM
[dbo].[MyTable] AS mt
WHERE
(@aVariable IS NULL OR mt.Date1 >= CAST(@date1 AS DATE))
AND (@aSecondVariable IS NULL OR mt.Date1 < DATEADD(day, 1, CAST(@date1 AS DATE)))
AND (@aThirdVariable IS NULL OR mt.Date2 >= CAST(@date2 AS DATE))
AND (@aFourtVariable IS NULL OR mt.Date2 < DATEADD(day, 1, CAST(@date2 AS DATE)))
AND ( @status IS NULL
OR @status <> 2
OR ( @status = 2
AND NOT EXISTS
( SELECT *
FROM dbo.MyTable AS mt2
WHERE mt2.CaseID = mt.CaseID
AND mt2.Date1 > mt.Date1
)
)
)
OPTION (RECOMPILE) ;
Run Code Online (Sandbox Code Playgroud)
@status可以写得稍微紧凑一些。我认为这不会对性能产生太大影响(如果有的话),它可能看起来很模糊,但为了完整起见,我也添加了这个版本:
SELECT
mt.ID
FROM
[dbo].[MyTable] AS mt
WHERE
(@aVariable IS NULL OR mt.Date1 >= CAST(@date1 AS DATE))
AND (@aSecondVariable IS NULL OR mt.Date1 < DATEADD(day, 1, CAST(@date1 AS DATE)))
AND (@aThirdVariable IS NULL OR mt.Date2 >= CAST(@date2 AS DATE))
AND (@aFourtVariable IS NULL OR mt.Date2 < DATEADD(day, 1, CAST(@date2 AS DATE)))
AND NOT EXISTS
( SELECT *
FROM dbo.MyTable AS mt2
WHERE @status = 2
AND mt2.CaseID = mt.CaseID
AND mt2.Date1 > mt.Date1
)
OPTION (RECOMPILE) ;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2993 次 |
| 最近记录: |