因为nvarchar 的datatype precedence 比varchar 高,所以它需要将列隐式转换为nvarchar,这样可以防止索引查找。
在某些排序规则下,它仍然可以使用查找并将cast 推送到针对查找匹配的行的剩余谓词中(而不是需要通过扫描对整个表中的每一行执行此操作)但大概你没有使用这样的排序规则。
排序规则对此的影响如下图所示。使用 SQL 排序规则时,您会得到扫描,对于 Windows 排序规则,它会调用内部函数 GetRangeThroughConvert 并能够将其转换为搜索。
CREATE TABLE [dbo].[phone]
(
phone1 VARCHAR(500) COLLATE sql_latin1_general_cp1_ci_as CONSTRAINT uq1 UNIQUE,
phone2 VARCHAR(500) COLLATE latin1_general_ci_as CONSTRAINT uq2 UNIQUE,
);
SELECT phone1 FROM [dbo].[phone] WHERE phone1 = N'5554474477';
SELECT phone2 FROM [dbo].[phone] WHERE phone2 = N'5554474477';
SHOWPLAN_TEXT 在下方
查询 1
|--Index Scan(OBJECT:([tempdb].[dbo].[phone].[uq1]), WHERE:(CONVERT_IMPLICIT(nvarchar(500),[tempdb].[dbo].[phone].[phone1],0)=CONVERT_IMPLICIT(nvarchar(4000),[@1],0)))
查询 2
|--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1005], [Expr1006], [Expr1004]))
|--Compute Scalar(DEFINE:(([Expr1005],[Expr1006],[Expr1004])=GetRangeThroughConvert([@1],[@1],(62))))
| |--Constant Scan
|--Index Seek(OBJECT:([tempdb].[dbo].[phone].[uq2]), SEEK:([tempdb].[dbo].[phone].[phone2] > [Expr1005] AND [tempdb].[dbo].[phone].[phone2] < [Expr1006]), WHERE:(CONVERT_IMPLICIT(nvarchar(500),[tempdb].[dbo].[phone].[phone2],0)=[@1]) ORDERED FORWARD)
在第二种情况下,计算标量emits the following values
Expr1004 = 62
Expr1005 = '5554474477'
Expr1006 = '5554474478'
计划中显示的搜索谓词在phone2 > Expr1005 and phone2 < Expr1006 上,因此从表面上看,它会排除'5554474477',但标志62 表示这确实匹配。