TSQL 通配符性能:可变或特定长度查询的结果
TSQL wildcard performance: results of variable or specific length query
在 table 中,我在列 ID (VARCHAR) 上有一个索引。
鉴于存在索引,我希望
Select Top 1 * from table where ID like 'abc%' order by ID desc
要非常快。
然后我做了一个
set statistics io on
对比发现
Select Top 1 * from table where ID like 'abc___' order by ID desc
也非常快(读取次数相同,在我的例子中为 5)。实际执行计划也显示完全相同,两种情况下的索引查找和键查找。
据我了解,它应该无法使用索引,因此读取次数要多得多。
随着索引的排序,'abc%' 它应该能够跳转到索引的末尾,那里有以 abc 开头的匹配项。但是我用 'abc___' 要求一个特定的长度,它不应该能够直接跳到索引中的某个地方,而是应该扫描所有以 'abc' 开头的条目的长度。
table 中有几个 1000 条这种格式的条目。
为什么查询 'abc___' 和查询 'abc%' 一样快?
为了复制这个,我设置了一个简单的环境复制:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T (ID VARCHAR(8) NOT NULL PRIMARY KEY);
INSERT #T (ID)
SELECT TOP 100000 123000 + CONVERT(VARCHAR(6), ROW_NUMBER() OVER(ORDER BY a.object_id))
FROM sys.all_objects a, sys.all_objects b
UNION ALL
SELECT TOP 100000 12300000 + CONVERT(VARCHAR(6), ROW_NUMBER() OVER(ORDER BY a.object_id))
FROM sys.all_objects a, sys.all_objects b;
SELECT COUNT(*)
FROM #T
WHERE ID LIKE '123%';
SELECT COUNT(*)
FROM #T
WHERE ID LIKE '123___';
查看执行计划时,如您所说,两者使用相同的索引查找:
如果您进一步检查该计划,您会发现两者都以相同的搜索范围开始:
<SeekPredicateNew>
<SeekKeys>
<StartRange ScanType="GE">
<RangeColumns>
<ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[#T]" Column="ID" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'123'">
<Const ConstValue="'123'" />
</ScalarOperator>
</RangeExpressions>
</StartRange>
<EndRange ScanType="LT">
<RangeColumns>
<ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[#T]" Column="ID" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'124'">
<Const ConstValue="'124'" />
</ScalarOperator>
</RangeExpressions>
</EndRange>
</SeekKeys>
</SeekPredicateNew>
两种搜索在幕后的唯一区别是第二种,其中一种通过 LIKE '123%'
进一步过滤,另一种通过 LIKE '123___'
.
进一步过滤
我想你的误解可能是索引查找必须一次性正确识别记录。事实并非如此,在第二次查询的情况下,索引查找将找到正确的范围,然后进一步的过滤器将从该范围中识别相关行。
为什么你认为 'abc%' 它应该能够跳到索引的末尾。
索引确实已排序,但引擎仍必须检查各个值以找到第一个不匹配的值 'abc%'。在找到它之前,它不知道该值是什么。下一个值可以是 'abd'、'abf'、'ax'.
在 table 中,我在列 ID (VARCHAR) 上有一个索引。
鉴于存在索引,我希望
Select Top 1 * from table where ID like 'abc%' order by ID desc
要非常快。
然后我做了一个
set statistics io on
对比发现
Select Top 1 * from table where ID like 'abc___' order by ID desc
也非常快(读取次数相同,在我的例子中为 5)。实际执行计划也显示完全相同,两种情况下的索引查找和键查找。
据我了解,它应该无法使用索引,因此读取次数要多得多。
随着索引的排序,'abc%' 它应该能够跳转到索引的末尾,那里有以 abc 开头的匹配项。但是我用 'abc___' 要求一个特定的长度,它不应该能够直接跳到索引中的某个地方,而是应该扫描所有以 'abc' 开头的条目的长度。
table 中有几个 1000 条这种格式的条目。
为什么查询 'abc___' 和查询 'abc%' 一样快?
为了复制这个,我设置了一个简单的环境复制:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T (ID VARCHAR(8) NOT NULL PRIMARY KEY);
INSERT #T (ID)
SELECT TOP 100000 123000 + CONVERT(VARCHAR(6), ROW_NUMBER() OVER(ORDER BY a.object_id))
FROM sys.all_objects a, sys.all_objects b
UNION ALL
SELECT TOP 100000 12300000 + CONVERT(VARCHAR(6), ROW_NUMBER() OVER(ORDER BY a.object_id))
FROM sys.all_objects a, sys.all_objects b;
SELECT COUNT(*)
FROM #T
WHERE ID LIKE '123%';
SELECT COUNT(*)
FROM #T
WHERE ID LIKE '123___';
查看执行计划时,如您所说,两者使用相同的索引查找:
如果您进一步检查该计划,您会发现两者都以相同的搜索范围开始:
<SeekPredicateNew>
<SeekKeys>
<StartRange ScanType="GE">
<RangeColumns>
<ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[#T]" Column="ID" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'123'">
<Const ConstValue="'123'" />
</ScalarOperator>
</RangeExpressions>
</StartRange>
<EndRange ScanType="LT">
<RangeColumns>
<ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[#T]" Column="ID" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'124'">
<Const ConstValue="'124'" />
</ScalarOperator>
</RangeExpressions>
</EndRange>
</SeekKeys>
</SeekPredicateNew>
两种搜索在幕后的唯一区别是第二种,其中一种通过 LIKE '123%'
进一步过滤,另一种通过 LIKE '123___'
.
我想你的误解可能是索引查找必须一次性正确识别记录。事实并非如此,在第二次查询的情况下,索引查找将找到正确的范围,然后进一步的过滤器将从该范围中识别相关行。
为什么你认为 'abc%' 它应该能够跳到索引的末尾。
索引确实已排序,但引擎仍必须检查各个值以找到第一个不匹配的值 'abc%'。在找到它之前,它不知道该值是什么。下一个值可以是 'abd'、'abf'、'ax'.