New*_*DBA 3 sql-server sql-server-2012 query-performance performance-tuning
对于我们的一个数据库,如下所示的查询非常慢。
由于安全原因,我无法分享实际的查询或计划,但只是想知道如何编写查询如下
SELECT [Id]
,[AboutMe]
,[Age]
,[CreationDate]
,[DisplayName]
,[DownVotes]
,[EmailHash]
,[LastAccessDate]
,[Location]
,[Reputation]
,[UpVotes]
,[Views]
,[WebsiteUrl]
,[AccountId]
FROM [StackOverflow2010].[dbo].[Users]
WHERE DisplayName IN (
SELECT DisplayName from dbo.Users
WHERE CAST(LastAccessDate AS DATE) = CAST ('20160814' AS DATE)
AND CreationDate>= DATEADD (DAY, -30,LastAccessDate)
AND CreationDate<= LastAccessDate)
Run Code Online (Sandbox Code Playgroud)
在 stackoverflow 数据库中,这不会返回任何行,但对于我们现有的 6 TBS 数据库,它真的很慢。
CreationDate
和LastAccessdate
列都是日期时间 (10)
和 DisplayName
是VARCHAR(50)
如果以上可以重写,请建议我如何提高性能
据我所知,只需添加索引,您就可以在不重写查询的情况下大大改进事情。
这是我在 SQL Server 2019 上得到的执行计划(请注意,我将日期更改为'20090814'
只是为了返回一些结果):
Table 'Users'. Scan count 18, logical reads 15430
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 94 ms.
Run Code Online (Sandbox Code Playgroud)
这将扫描整个 Users 表一次以获取满足日期条件的用户,然后再扫描几次以获取具有匹配显示名称的其余用户。
不理想。
查询在我的机器上只需要 94 毫秒,主要是因为所有内容都在 RAM 中,并且查询以并行度 (DOP) 8 运行。
但是,添加这两个索引对情况有很大帮助:
CREATE NONCLUSTERED INDEX IX_LastAccessDate
ON dbo.Users (LastAccessDate)
INCLUDE (CreationDate, DisplayName);
CREATE NONCLUSTERED INDEX IX_DisplayName
ON dbo.Users (DisplayName);
Run Code Online (Sandbox Code Playgroud)
现在我得到了这个执行计划:
CREATE NONCLUSTERED INDEX IX_LastAccessDate
ON dbo.Users (LastAccessDate)
INCLUDE (CreationDate, DisplayName);
CREATE NONCLUSTERED INDEX IX_DisplayName
ON dbo.Users (DisplayName);
Run Code Online (Sandbox Code Playgroud)
SQL Server能够生产计划寻求通过在这里使用内部GetRangeThroughConvert
函数,以确定可能的范围datetime
等效于值CAST(LastAccessDate AS DATE) = CAST ('20160814' AS DATE)
。本质上,它在后台重写查询以匹配Mo64 提出的建议。
注意:您最好重写以显式使用该范围查询,而不是依赖于隐藏的隐式转换
您可以在 Paul White 的博客上阅读有关此类执行计划的更多详细信息:Dynamic Seeks and Hidden Implicit Conversions
如果您消除键查找,这种情况可能会得到进一步改善 - 通过选择较少的列,或在第一个索引中包含所有必需的列 (on LastAccessDate
)。
我意识到这是一个玩具示例,但希望它说明了可以应用于您的实际情况的一般解决方案:
CAST
从datetime
到date
是两个不等式条件。换句话说,替换了这个:WHERE CAST(LastAccessDate AS DATE) = CAST ('20160814' AS DATE)
Run Code Online (Sandbox Code Playgroud)
有了这个:
WHERE LastAccessDate >= '20160814' AND LastAccessDate < '20160815'
Run Code Online (Sandbox Code Playgroud)
varchar(50)
列值 归档时间: |
|
查看次数: |
489 次 |
最近记录: |