小编典典

为什么用phone = N'1234'的查询要比phone ='1234'慢?

sql

我有一个字段是varchar(20)

执行此查询时,它很快(使用索引查找):

SELECT * FROM [dbo].[phone] WHERE phone = '5554474477'

但是这一步很慢(使用索引扫描)。

SELECT * FROM [dbo].[phone] WHERE phone = N'5554474477'

我猜想如果我将字段更改为nvarchar,则它将使用索引查找。


阅读 182

收藏
2021-04-15

共1个答案

小编典典

因为nvarchar具有更高的数据类型优先级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)

在第二种情况下,计算标量会发出以下值

Expr1004 = 62
Expr1005 = '5554474477'
Expr1006 = '5554474478'

计划中显示的搜索谓词处于打开状态,phone2 > Expr1005 and phone2 < Expr1006因此从表面上将要排除,'5554474477'但标记62表示确实匹配。

2021-04-15