J.D*_*.D. 10 sql-server optimization execution-plan sql-server-2016
例如,如果我点击 sys 动态视图来选择一个特定的查询计划,我是否能够将该查询计划插入到另一个数据库的计划缓存中,运行相同的查询?(我知道查询经过哈希处理并进行比较以确定何时生成新计划,因此在我的示例中,我将确保查询确实是逐个字符完全相同的。)
AMt*_*two 11
好的,这就是您问题的答案,但您可能想知道为什么。
不同的数据库可能有不同的数据,因此需要不同的计划。
假设您将WideWorldImporters数据库恢复到同一台服务器两次(我们称之为WWI_1和WWI_2)。您有两个相同的数据库。SQL Server 可能会创建一个计划并将其用于两个数据库的查询中。
但问题是,这两个数据库一上线,它们的“相同”就分叉了。它们可以独立更改。即使它们维护相同的模式,它们的数据也可以独立更改。因此,SQL Server在编译计划时必须独立考虑数据库。WWI_1可以有不同的统计从WWI_2,这可能会导致不同的计划。为了让 SQL Server 对两个数据库使用相同的计划,SQL Server 需要在分叉后跟踪差异——这比仅仅编译/维护单独的计划更复杂和更昂贵。
让我们假设您是一家软件公司,并为您的客户托管软件。每个客户都有自己的数据库。在您的托管环境中,您可能在每台服务器上都有数百个数据库,其中每个数据库都具有相同的架构,但每个客户端的数据都是唯一的。
一位客户拥有95% 加州客户的客户群。查询加利福尼亚州所有客户的地址表将导致表扫描。
-- For CustomerA this query returns 95% of the table, so it scans
SELECT CustomerID
FROM dbo.Addresses
WHERE StateCode = 'CA';
Run Code Online (Sandbox Code Playgroud)
不同的客户拥有均匀分布在美国每个州的客户群,并且还拥有重要的国际业务。相对较小比例的客户来自加利福尼亚。在这种情况下,查询加利福尼亚州所有客户的地址表将导致表seek。
-- For CustomerB this query returns <1% of the table, so it seeks
SELECT CustomerID
FROM dbo.Addresses
WHERE StateCode = 'CA';
Run Code Online (Sandbox Code Playgroud)
相同的查询,在具有相同模式的数据库上,由于不同的统计数据而具有完全不同的计划。即使这两个数据库最初都是从同一个源备份中恢复的,SQL Server 也需要编译不同的计划。
两个查询 100% 相同并且架构 100% 相同是不够的。仅基于这些标准的重用计划可能是非常错误的——因此 SQL Server 不会这样做。
呃……有点。
让我们使用相同的“您是一家软件公司,并为您的客户托管软件”示例。无论 SQL Server 想要做什么,您都希望在每个托管数据库中强制执行相同的计划。您可以使用计划指南并将相同的指南应用于服务器上的每个数据库。这与“将 [一个] 查询计划插入另一个数据库的计划缓存中”不太一样……但实际上是一回事。
类似的东西SELECT COUNT(*) FROM dbo.SomeTable很简单,可以在两个数据库上使用相同的计划,对吗?不,即使那样!
让我们创建一个示例:
这是一些代码来做到这一点:
CREATE DATABASE Sample1;
GO
USE Sample1
GO
CREATE TABLE dbo.SomeTable (
SomeID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
StuffType int,
OtherStuff varchar(100),
INDEX OtherStuff(OtherStuff)
);
GO
SET NOCOUNT ON;
INSERT INTO dbo.SomeTable (StuffType,OtherStuff)
SELECT object_id%50,
name
FROM sys.objects;
GO 1000
BACKUP DATABASE Sample1 TO DISK = '/var/opt/mssql/data/Sample1.bak' WITH INIT;
RESTORE DATABASE Sample2 FROM DISK = '/var/opt/mssql/data/Sample1.bak'
WITH MOVE 'Sample1' TO '/var/opt/mssql/data/Sample2.mdf',
MOVE 'Sample1_log' TO '/var/opt/mssql/data/Sample2_log.ldf';
Run Code Online (Sandbox Code Playgroud)
现在,让我们做这个COUNT(*)查询。两者的计划相同吗?是的!在我的笔记本电脑上,它通过扫描集群 PK 来执行计数

SQL Server 选择扫描 PK,因为它更小。非聚集索引较大,因为它高度碎片化。
现在索引维护发生在Sample1数据库上(但不是Sample2)。有人重建索引dbo.SomeTable:
ALTER INDEX ALL ON Sample1.dbo.SomeTable REBUILD;
Run Code Online (Sandbox Code Playgroud)
在 上Sample1,非聚集索引现在很好且紧凑。它比PK小。扫描非聚集索引将是`Sample1 的正确选择,因为它更小,IO 更少,速度更快。
在 上Sample2,扫描 PK 是正确的选择,因为它更小,IO 更少,因此速度更快。
具有相同数据的相同模式,第二个从第一个的最近备份中分叉出来。这两个查询都有不同的执行计划,但都有最适合他们场景的查询计划。