创建替换字符串查询的函数会减慢存储过程的速度

时间:2018-06-05 06:21:47

标签: sql-server tsql

我创建了这个存储过程:

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 :(得分:1)

@ lad2025发布了什么是要走的路。也就是说,如果计算/持久列不是一个选项,您可以将标量udf转换为内联表值函数(itvf)。 itvf的表现要比用户定义的标量函数(标量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;

答案 1 :(得分:0)

而不是自己的功能和嵌套多个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