我有一个名为Data:
的xml列的表CREATE TABLE [dbo].[Users](
[UserId] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [nvarchar](max) NOT NULL,
[LastName] [nvarchar](max) NOT NULL,
[Email] [nvarchar](250) NOT NULL,
[Password] [nvarchar](max) NULL,
[UserName] [nvarchar](250) NOT NULL,
[LanguageId] [int] NOT NULL,
[Data] [xml] NULL,
[IsDeleted] [bit] NOT NULL,...
在数据列中有这个xml
<data>
<RRN>...</RRN>
<DateOfBirth>...</DateOfBirth>
<Gender>...</Gender>
</data>
现在,执行此查询:
SELECT UserId FROM Users
WHERE data.value('(/data/RRN)[1]', 'nvarchar(max)') = @RRN
清除缓存后(如果我在彼此之后执行几次)910,739,630,635,...... ms。
现在,一位数据库专家告诉我,添加一个函数,一个视图和更改查询会使搜索具有给定RRN的用户更快。但是,相反,当我执行数据库专家的更改时,这些是结果:2584,2342,2322,2383,......
这是增加的功能:
CREATE FUNCTION dbo.fn_Users_RRN(@data xml)
RETURNS nvarchar(100)
WITH SCHEMABINDING
AS
BEGIN
RETURN @data.value('(/data/RRN)[1]', 'varchar(max)');
END;
添加的观点:
CREATE VIEW vwi_Users
WITH SCHEMABINDING
AS
SELECT UserId, dbo.fn_Users_RRN(Data) AS RRN from dbo.Users
索引:
CREATE UNIQUE CLUSTERED INDEX cx_vwi_Users ON vwi_Users(UserId)
CREATE NONCLUSTERED INDEX cx_vwi_Users__RRN ON vwi_Users(RRN)
然后更改了查询:
SELECT UserId FROM Users
WHERE dbo.fn_Users_RRN(Data) = @RRN
为什么具有功能和视图的解决方案会变慢?
答案 0 :(得分:2)
该观点的目的是将XML值预先计算为常规列。然后,要在视图的索引中使用该预先计算的值,您是否应该实际查询视图?
SELECT
UserId
FROM vwi_Users
WHERE RRN= '59021626919-61861855-S_FA1E11'
另外,将索引设为:
CREATE NONCLUSTERED INDEX cx_vwi_Users__RRN ON vwi_Users(RRN) INCLUDE (UserId)
它被称为覆盖索引,因为查询中所需的所有列都在索引中。
答案 1 :(得分:2)
您是否曾尝试将该功能结果添加到表(不是视图)作为持久的计算列?
ALTER TABLE dbo.Users
ADD dbo.fn_Users_RRN(Data) PERSISTED
这样做将从XML中提取该信息,将其存储在计算的,始终最新的列中,并且持久化标志使其物理存储在表中其他列的旁边。
如果这样做(PURISTED标志就其所有的限制而言有点不确定),那么你应该看到与查询表上任何其他字符串字段几乎相同的性能......如果计算列是PERSISTED,如果您觉得有必要,甚至可以在其上加上索引。
答案 2 :(得分:1)
检查查询执行计划并确认新查询是否甚至使用该视图。如果查询不使用视图,那就是问题。
这个查询如何公平?
SELECT UserId FROM vwi_Users
WHERE RRN = '59021626919-61861855-S_FA1E11'
我看到你正在自由地混合nvarchar和varchar。不要那样做!它可以导致完整的索引转换(eeeeevil)。
答案 3 :(得分:0)
标量函数往往在SQL Server中表现很差。我不确定为什么如果你把它作为一个持久的计算列并对其进行索引,它与普通的索引列没有相同的性能,但它可能是由于UDF被调用,即使你认为不再需要它计算数据后调用。
我认为你从另一个答案中知道这一点,但是你的最终查询错误地在每一行上调用了标量UDF(打败了持续计算的点):
SELECT UserId FROM Users
WHERE dbo.fn_Users_RRN(Data) = @RRN
应该是
SELECT UserId FROM vwi_Users
WHERE RNN = @RRN