我有一个字段是varchar(20)
执行此查询时,它很快(使用索引查找):
SELECT * FROM [dbo].[phone] WHERE phone = '5554474477'
但是这一步很慢(使用索引扫描)。
SELECT * FROM [dbo].[phone] WHERE phone = N'5554474477'
我猜想如果我将字段更改为nvarchar,则它将使用索引查找。
因为nvarchar具有更高的数据类型优先级,varchar所以它需要对列执行隐式转换nvarchar,从而防止了索引查找。
nvarchar
varchar
在某些排序规则下,它仍然可以使用搜索并将其推cast入残差谓词,使其与搜索所匹配的行相对(而不是需要通过扫描对整个表中的每一行都执行此操作),但大概不是使用这样的排序规则。
cast
整理对此的影响如下所示。使用SQL排序规则时,您会进行扫描,对于Windows排序规则,它将调用内部函数GetRangeThroughConvert并将其转换为搜索。
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下面是
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)
在第二种情况下,计算标量会发出以下值
Expr1004 = 62 Expr1005 = '5554474477' Expr1006 = '5554474478'
计划中显示的搜索谓词处于打开状态,phone2 > Expr1005 and phone2 < Expr1006因此从表面上将要排除,'5554474477'但标记62表示确实匹配。
phone2 > Expr1005 and phone2 < Expr1006
'5554474477'
62