Создание функции для замены строкового запроса замедляет хранимую процедуру - PullRequest
0 голосов
/ 05 июня 2018

Я создал эту хранимую процедуру:

CREATE PROCEDURE [dbo].[GetYearsByMake] 
    @Make VARCHAR(50)
AS
BEGIN
   SELECT DISTINCT [year] 
   FROM make_model 
   WHERE active = 1 
     AND isUnique = 1 
     AND [dbo].[ReplaceUrlEscapeChars](make) = @Make 
   ORDER BY [year] DESC
END

, и ReplaceUrlEscapeChars - это функция, которую я создал, чтобы заменить данные, содержащие специальный символ, на один -

CREATE FUNCTION [dbo].[ReplaceUrlEscapeChars] 
    (@MyString AS VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
    IF (@MyString LIKE '%[^0-9a-zA-Z\s-]%')
    BEGIN
        SET @MyString = Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace
            (Replace(Replace(Replace(Replace(@MyString,'<','-'),'>','-'),'#','-'),'{','-'),'}','-'),'|','-'),'\','-'),'^','-'),'~','-'),'[','-'),']','-'),';','-')
            ,':','-'),'@','-'),'&','-'),'$','-'),'/','-'),'.','-');
    END

    SET @MyString = LTRIM(RTRIM(@MyString));
    RETURN @MyString
END

, но я заменяюкаждая строка с этой функцией в запросе, запрос для получения данных выполняется медленно.

Как я могу оптимизировать это?

Есть ли другой способ получить данные быстрым способом

Ответы [ 2 ]

0 голосов
/ 05 июня 2018

То, что написал @ lad2025, это путь.Тем не менее, в тех случаях, когда вычисляемый / сохраняемый столбец не является вариантом, вам будет полезно превратить ваш скалярный udf в встроенную табличную функцию (itvf).itvf работает намного лучше, чем пользовательские скалярные функции (scalar udf) по ряду причин, включая тот факт, что они не убивают параллелизм, как скалярные функции.

Вы можете переписать ваш скалярный udf как itvf, например:

CREATE FUNCTION dbo.itvfReplaceUrlEscapeChars(@MyString as varchar(Max))
RETURNS TABLE AS RETURN
SELECT 
  CASE WHEN @MyString LIKE '%[^0-9a-zA-Z\s-]%' 
       THEN LTRIM(RTRIM(
            Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(
            Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(
            @MyString,'<','-'),'>','-'),'#','-'),'{','-'),'}','-'),'|','-'),'\','-')
            ,'^','-'),'~','-'),'[','-'),']','-'),';','-'),':','-'),'@','-'),'&','-')
            ,'$','-'),'/','-'),'.','-')))
       ELSE LTRIM(RTRIM(@MyString)) 
  END AS cleanedString;
GO

Тогда вы бы назвали это так:

CREATE PROC dbo.GetYearsByMake @Make VARCHAR(50)
AS
BEGIN
  SELECT DISTINCT [year] 
  FROM make_model
  CROSS APPLY dbo.itvfReplaceUrlEscapeChars(make)
  WHERE active=1 AND isUnique=1 
  AND cleanedString = @Make 
  ORDER BY [year] DESC;
END

Стоит также отметить, что работас типами данных varchar (max) это очень дорого.Если вам удастся избежать использования varchar (8000) или менее, вы также увидите значительное повышение производительности.Иметь отфильтрованный некластеризованный индекс для make_model, который выглядит примерно так, вероятно, тоже поможет:

CREATE NONCLUSTERED INDEX nc_make_model_poc ON make_model(year)
WHERE active = 1 AND isUnique = 1;
0 голосов
/ 05 июня 2018

Вместо собственной функции и нескольких вложенных REPLACE я бы встроил TRANSLATE - SQL Server 2017+:

CREATE PROCEDURE  [dbo].[GetYearsByMake] @Make VARCHAR(50)
AS
BEGIN
  SELECT DISTINCT [year] 
  FROM make_model
  WHERE active=1 AND isUnique=1 
    AND TRANSLATE(make, '<>#{}|','------') = @Make 
  ORDER BY [year] DESC;
END

DBFiddle Demo


Немного лучший подход - добавить сгенерированный постоянный столбец с именем normalized_make и создать и индексировать поверх него.

ALTER TABLE make_model
ADD normalized_make AS 
  (CAST(TRIM(TRANSLATE(make, '<>#{}|','------')) AS VARCHAR(100))) PERSISTED;

CREATE INDEX indx_name ON make_model(normalized_make);

SELECT *
FROM make_model
WHERE normalized_make = @Make;

РЕДАКТИРОВАТЬ:

ALTER TABLE make_model
ADD normalized_make AS 
  (CAST(RTRIM(LTRIM(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace
            (Replace(Replace(Replace(Replace(Make,'<','-'),'>','-'),'#','-'),'{','-'),'}','-'),'|','-'),'\','-'),'^','-'),'~','-'),'[','-'),']','-'),';','-')
            ,':','-'),'@','-'),'&','-'),'$','-'),'/','-'),'.','-'))) AS VARCHAR(100))) PERSISTED;

DBFiddle Demo2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...