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'.