Where 子句索引扫描 - 索引查找

Ome*_*mer 5 index sql-server execution-plan

我有下表:

CREATE TABLE Test
(
    Id int IDENTITY(1,1) NOT NULL,
    col1 varchar(37) NULL,
    testDate datetime NULL 
)

GO

insert Test    
select null
go 700000 

insert Test
select cast(NEWID() as varchar(37))
go 300000
Run Code Online (Sandbox Code Playgroud)

以及以下索引:

create clustered index CIX on Test(ID)
create nonclustered index IX_RegularIndex on Test(col1)
create nonclustered index IX_RegularDateIndex on Test(testDate)
Run Code Online (Sandbox Code Playgroud)

当我在我的桌子上查询时:

SET STATISTICS IO ON
select * from Test  where col1=NEWID()
select * from Test  where TestDate=GETDATE()
Run Code Online (Sandbox Code Playgroud)

首先是进行索引扫描,而第二个索引是搜索。我希望他们都必须进行索引查找。为什么先进行索引扫描?

在此处输入图片说明

Paw*_*ajs 7

这里的问题是到UNIQUEIDENTIFIER类型的隐式转换。执行计划中有关于它的警告,它与Data Type Precedence 相关联

您的表使用了错误的数据类型来存储 GUID。如果col1正确输入为uniqueidentifier,您的问题就不会出现。GUID的字符串表示的最大长度为 36(不是 37!)个字符,这比使用uniqueidentifier(16 个字节)效率低得多。另请注意,NEWID()返回 a uniqueidentifier,而不是任何类型的字符串。

我在下面的查询中添加了一些提示以获得相同的执行计划。

带警告的执行计划

如果您分配NEWID()给一个变量,但您仍然必须声明VARCHAR(37)变量或只是在 where 子句中强制NEWID()转换VARCHAR(37),则可以避免这种情况。

以下是一些向我们展示其工作原理的查询:

-- Query 1
SELECT * FROM Test WITH(INDEX=[IX_RegularIndex]) WHERE col1=NEWID()
GO

-- Query 2
DECLARE @id UNIQUEIDENTIFIER = NEWID()

SELECT * FROM Test WITH(INDEX=[IX_RegularIndex]) WHERE col1=@id
GO

-- Query 3
DECLARE @id VARCHAR(37) = NEWID()

SELECT * FROM Test WITH(INDEX=[IX_RegularIndex]) WHERE col1=@id
GO

-- Query 4
SELECT * FROM Test WITH(INDEX=[IX_RegularIndex]) WHERE col1=cast(NEWID() as varchar(37))
GO
Run Code Online (Sandbox Code Playgroud)

查询 1 - 查询 2 查询 3 查询 4

关于NEWID函数求值次数的相关问答:

加入的虚拟表中的 NEWID() 导致意外的交叉应用行为