我很确定表定义与此相似:
CREATE TABLE dbo.households
(
tempId integer NOT NULL,
n integer NOT NULL,
HHID integer IDENTITY NOT NULL,
CONSTRAINT [UQ dbo.households HHID]
UNIQUE NONCLUSTERED (HHID),
CONSTRAINT [PK dbo.households tempId, n]
PRIMARY KEY CLUSTERED (tempId, n)
);
CREATE TABLE dbo.persons
(
tempId integer NOT NULL,
sporder integer NOT NULL,
n integer NOT NULL,
PERID integer IDENTITY NOT NULL,
HHID integer NOT NULL,
CONSTRAINT [UQ dbo.persons HHID]
UNIQUE NONCLUSTERED (PERID),
CONSTRAINT [PK dbo.persons tempId, n, sporder]
PRIMARY KEY CLUSTERED (tempId, n, sporder)
);
我没有这些表或您的数据的统计信息,但以下内容至少可以设置正确的表基数(页数是一个猜测):
UPDATE STATISTICS dbo.persons
WITH
ROWCOUNT = 5239842,
PAGECOUNT = 100000;
UPDATE STATISTICS dbo.households
WITH
ROWCOUNT = 1928783,
PAGECOUNT = 25000;
查询计划分析
您现在拥有的查询是:
UPDATE P
SET HHID = H.HHID
FROM dbo.households AS H
JOIN dbo.persons AS P
ON P.tempId = H.tempId
AND P.n = H.n;
这会产生效率低下的计划:
该计划中的主要问题是哈希联接和排序。两者都需要内存授权(哈希联接需要构建哈希表,排序需要空间以在排序进行时存储行)。计划资源管理器显示此查询已被授予765 MB:
这是用于一个查询的大量服务器内存!更重要的是,基于行计数和大小估计,此内存授予在执行开始之前是固定的。
如果在执行时发现内存不足,则至少一些用于哈希和/或排序的数据将被写入物理tempdb磁盘。这被称为“溢出”,可能是非常缓慢的操作。您可以使用Profiler事件“ 哈希警告”和“ 排序警告”来跟踪这些溢出(在SQL Server 2008中)。
哈希表的构建输入的估算值非常好:
排序输入的估算值不太准确:
您将不得不使用Profiler进行检查,但是在这种情况下,我怀疑这种排序会溢出到tempdb中。散列表也有可能溢出,但这不太明确。
请注意,为此查询保留的内存在哈希表和排序之间分配,因为它们同时运行。“内存分数”计划属性显示每个操作预期使用的内存授权的相对数量。
为什么排序和散列?
查询优化器引入了排序,以确保行以聚集键顺序到达聚集索引更新运算符。这促进了对表的顺序访问,这通常比随机访问要高效得多。
哈希联接是一个不太明显的选择,因为它的输入大小相似(无论如何都是一阶近似)。当一个输入(用于构建哈希表的输入)相对较小时,散列联接是最佳的。
在这种情况下,优化程序的成本核算模型确定哈希联接是三个选项(哈希,合并,嵌套循环)中最便宜的。
改善绩效
成本模型并不一定总是正确。它倾向于高估并行合并联接的成本,尤其是随着线程数量的增加。我们可以通过查询提示强制进行合并联接:
UPDATE P
SET HHID = H.HHID
FROM dbo.households AS H
JOIN dbo.persons AS P
ON P.tempId = H.tempId
AND P.n = H.n
OPTION (MERGE JOIN);
这将产生一个不需要太多内存的计划(因为合并联接不需要哈希表):
有问题的排序仍然存在,因为合并联接仅保留其联接键(tempId,n)的顺序,而聚簇键为(tempId,n,sporder)。您可能会发现合并联接计划的执行不比哈希联接计划好。
嵌套循环联接
我们也可以尝试嵌套循环联接:
UPDATE P
SET HHID = H.HHID
FROM dbo.households AS H
JOIN dbo.persons AS P
ON P.tempId = H.tempId
AND P.n = H.n
OPTION (LOOP JOIN);
该查询的计划是:
该查询计划被优化程序的成本模型认为是最差的,但它确实具有一些非常理想的功能。首先,嵌套循环联接不需要内存授予。其次,它可以保留Persons
表中的键顺序,因此不需要显式排序。您可能会发现该计划的效果相对较好,甚至可能还不错。
并行嵌套循环
嵌套循环计划的最大缺点是它在单个线程上运行。该查询可能受益于并行性,但是优化器认为此处没有优势。这也不一定是正确的。不幸的是,没有内置的查询提示来获取并行计划,但是有一种未记录的方式:
UPDATE t1
SET t1.HHID = t2.HHID
FROM dbo.persons AS t1
INNER JOIN dbo.households AS t2
ON t1.tempId = t2.tempId AND t1.n = t2.n
OPTION (LOOP JOIN, QUERYTRACEON 8649);
通过QUERYTRACEON
提示启用跟踪标志8649 会产生以下计划:
现在,我们有了一个避免排序的计划,不需要为连接添加额外的内存,并有效地使用了并行性。您应该发现此查询的性能比其他查询好得多。
在我的文章《强制并行查询执行计划》中有关并行性的更多信息: