SQL-запрос сервера с использованием функции и представления медленнее - PullRequest
1 голос
/ 14 апреля 2010

У меня есть таблица со столбцом xml с именем Data:

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, ... мс.

Теперь специалист по БД сказал мне, что добавление функции, представления и изменения запроса значительно ускорит поиск пользователя с данным 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 ]

2 голосов
/ 14 апреля 2010

точка зрения состояла в том, чтобы предварительно вычислить значение 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)

он называетсяиндекс покрытия, так как все столбцы, необходимые в запросе, находятся в индексе.

2 голосов
/ 14 апреля 2010

Вы пытались добавить результат этой функции в вашу таблицу (не представление) как сохраненный вычисляемый столбец ??

ALTER TABLE dbo.Users
   ADD dbo.fn_Users_RRN(Data) PERSISTED

Это извлечет эту часть информации из XML, сохранит ее в вычисляемом, всегда актуальном столбце, а постоянный флаг сделает его физически сохраненным рядом с другими столбцами вашей таблицы.

Если это работает (флаг PERSISTED немного ненадежен с точки зрения всех имеющихся у него ограничений), то вы должны увидеть почти такую ​​же производительность, как и запрос любого другого строкового поля в вашей таблице ... и если вычисляемый столбец УДОВЛЕТВОРЕННЫЙ, вы даже можете поставить на него указатель, если вы чувствуете в этом необходимость.

1 голос
/ 14 апреля 2010

Проверьте план выполнения запроса и убедитесь, что новый запрос даже использует представление. Если запрос не использует представление, это проблема.


Насколько справедлив этот запрос?

SELECT UserId FROM vwi_Users
WHERE RRN = '59021626919-61861855-S_FA1E11'

Я вижу, вы свободно смешиваете nvarchar и varchar. Не делай этого! Это может вызвать полное преобразование индекса (eeeeevil).

0 голосов
/ 15 апреля 2010

Скалярные функции имеют тенденцию работать очень плохо в SQL Server. Я не уверен, почему, если вы сделаете это постоянным вычисляемым столбцом и проиндексируете его, он не будет иметь такую ​​же производительность, как обычный индексированный столбец, но это может быть связано с вызовом UDF, даже если вы думаете, что он больше не нужен вызываться после вычисления данных.

Я думаю, что вы знаете это из другого ответа, но ваш последний запрос ошибочно вызывает скалярную UDF в каждой строке (преодолевая точку сохранения вычислений):

SELECT UserId FROM Users  
WHERE dbo.fn_Users_RRN(Data) = @RRN 

Это должно быть

SELECT UserId FROM vwi_Users  
WHERE RNN = @RRN 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...