使用函数和视图的SQL Server查询速度较慢

时间:2010-04-14 19:10:37

标签: sql-server database performance

我有一个名为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

为什么具有功能和视图的解决方案会变慢?

4 个答案:

答案 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