它取决于表中的数据,索引....很难说,而无法比较执行计划/ io +时间统计信息。
我期望的区别是两个表之间的JOIN之前发生了额外的过滤。在我的示例中,我将更新更改为选择以重用表。
有“最优化”的执行计划
执行计划
您清楚地看到了过滤器操作的发生,在我的测试数据中,没有记录被过滤掉,因此也没有进行任何改进。
执行计划,无“优化”
执行计划
筛选器不见了,这意味着我们将不得不依靠联接来筛选出不需要的记录。
其他原因
更改查询的另一个原因/后果可能是,更改查询时创建了一个新的执行计划,该计划恰好更快。例如,引擎选择其他Join运算符,但这只是猜测。
编辑:
得到两个查询计划后澄清:
该查询正在从大表中读取550M行,并将其过滤掉。
这意味着谓词是执行大部分过滤的谓词,而不是搜索谓词。结果是读取了数据,但返回的次数较少。
使sql server使用其他索引(查询计划)/添加索引可以解决此问题。
那么,为什么优化查询没有同样的问题?
因为使用了不同的查询计划,所以使用扫描而不是搜索。
无需进行任何查找,但仅返回4M行即可使用。
下一个差异
忽略更新差异(优化查询中未更新任何内容),在优化查询中使用了哈希匹配:
而不是对未优化的嵌套循环联接:
当一个表较小而另一表较大时,嵌套循环最好。由于它们都接近相同的大小,因此我认为在这种情况下,哈希匹配是更好的选择。
总览
优化查询
优化查询的计划具有并行性,使用哈希匹配联接,并且需要执行更少的残留IO过滤。它还使用位图来消除不能产生任何联接行的键值。(也没有任何更新)
非优化查询
非优化查询的计划没有并行性,使用嵌套循环联接,并且需要对550M记录进行残留IO过滤。(也正在进行更新)
您可以采取什么措施来改善非优化查询?
但是由于使用函数且此表很大,因此这可能不是最佳解决方案。
- 更新统计信息,使用重新编译来尝试并获得更好的计划。
- 向
(HASH JOIN, MERGE JOIN)
查询添加OPTION
- ...
测试数据+使用的查询
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
应该在那里做您想做的事情,而无需列出所有字符并且拥有难以阅读的代码