Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL递归CTE replace语句太慢_Sql_Sql Server - Fatal编程技术网

SQL递归CTE replace语句太慢

SQL递归CTE replace语句太慢,sql,sql-server,Sql,Sql Server,我有一个递归CTE,它替换一个表达式中的多个值,但是当有许多表达式时,它太慢了 CREATE TABLE #table1(IdExpresion INT, expresion VARCHAR(MAX)) CREATE TABLE #table2(IdExpresion INT, searchExpresion VARCHAR(50), replacementExpresion VARCHAR(50)) INSERT INTO #table1(IdExpresion, expresion) VA

我有一个递归CTE,它替换一个表达式中的多个值,但是当有许多表达式时,它太慢了

CREATE TABLE #table1(IdExpresion INT, expresion VARCHAR(MAX))
CREATE TABLE #table2(IdExpresion INT, searchExpresion VARCHAR(50), replacementExpresion VARCHAR(50))

INSERT INTO #table1(IdExpresion, expresion)
VALUES(1, 'Mary had a little lamb'),
      (2, 'The new student, student_name has the following grades Math - math_grade, Science - Science_grade')

INSERT INTO #table2(IdExpresion, searchExpresion, replacementExpresion)
VALUES(1, 'lamb','dog'),
      (2, 'student_name','Joe Smith'),
      (2, 'math_grade','A'),
      (2, 'Science_grade','B+')

;WITH cte(IdExpresion, expresion, lvl) AS
(
    SELECT t1.IdExpresion, t1.expresion, 1
    FROM #table1 t1
    UNION ALL    
    SELECT cte.IdExpresion, REPLACE(cte.expresion, t2.searchExpresion, t2.replacementExpresion), cte.lvl + 1 
    FROM cte  
    INNER JOIN #table2 t2
    ON cte.IdExpresion = t2.IdExpresion
       AND CHARINDEX(t2.searchExpresion, cte.expresion) > 0
)
SELECT DISTINCT c2.expresion
FROM (SELECT IdExpresion, MAX(lvl) AS lvl
      FROM cte
      GROUP BY IdExpresion) c1
INNER JOIN cte c2
   ON c1.IdExpresion = c2.IdExpresion 
      AND c1.lvl = c2.lvl
OPTION (MAXRECURSION 0);

有人有什么建议吗?顺便说一句,我使用的是SQL Server,您可以在其中添加另一个CTE,该CTE为每次替换获取一个行号,并由IdExpresion分区

然后在递归CTE中,不是向上计数,而是向下计数,直到与替换行数不匹配

CTE中的最后一个条目(包含所有替换项)将具有0级。

这样,每次IdExpresion仅执行一次替换。
而不必使用CHARINDEX

您还可以将该搜索cte替换为临时表。
表2中的记录具有该行编号的记录。
这样做的好处是,可以使用表添加复合索引。
在大型表上,它应该加快到替换的递归连接

雷克斯试验机试验


您可以向其中添加另一个CTE,该CTE为每个替换获取一个行号,并由IdExpresion分区

然后在递归CTE中,不是向上计数,而是向下计数,直到与替换行数不匹配

CTE中的最后一个条目(包含所有替换项)将具有0级。

这样,每次IdExpresion仅执行一次替换。
而不必使用CHARINDEX

您还可以将该搜索cte替换为临时表。
表2中的记录具有该行编号的记录。
这样做的好处是,可以使用表添加复合索引。
在大型表上,它应该加快到替换的递归连接

雷克斯试验机试验


不确定是否还有更多的表现,但这里有一个蛮力方法只是为了好玩

已经有+1 LukStorm的答案了,我怀疑这是正确的方向

示例

Declare @S varchar(max) = (Select IdExpresion,expresion = replace(' '+expresion,' ',concat(' ',IdExpresion,'|||')) From #Table1 For XML Raw )

Select @S = replace(@S,concat(IdExpresion,'|||',searchExpresion),replacementExpresion) From  #table2

Select IdExpresion = B.i.value('@IdExpresion', 'int')
      ,expresion   = ltrim(replace(B.i.value('@expresion', 'varchar(max)'),B.i.value('@IdExpresion', 'varchar(25)')+'|||',''))
 From  (Select x = Cast(@S as xml).query('.')) as A 
 Cross Apply x.nodes('row') AS B(i)
返回

IdExpresion expresion
1           Mary had a little dog
2           The new student, Joe Smith has the following grades Math - A, Science - B+

不确定是否还有更多的表现,但这里有一个蛮力方法只是为了好玩

已经有+1 LukStorm的答案了,我怀疑这是正确的方向

示例

Declare @S varchar(max) = (Select IdExpresion,expresion = replace(' '+expresion,' ',concat(' ',IdExpresion,'|||')) From #Table1 For XML Raw )

Select @S = replace(@S,concat(IdExpresion,'|||',searchExpresion),replacementExpresion) From  #table2

Select IdExpresion = B.i.value('@IdExpresion', 'int')
      ,expresion   = ltrim(replace(B.i.value('@expresion', 'varchar(max)'),B.i.value('@IdExpresion', 'varchar(25)')+'|||',''))
 From  (Select x = Cast(@S as xml).query('.')) as A 
 Cross Apply x.nodes('row') AS B(i)
返回

IdExpresion expresion
1           Mary had a little dog
2           The new student, Joe Smith has the following grades Math - A, Science - B+
你好

这是另一个解决方案。请检查这是否符合您的需要。此解决方案不使用任何循环,而是使用简单的动态查询

DECLARE @SQLString nvarchar(MAX);
-- do not make mistake, this is simple CTE and not a recursive CTE (no Loop)
;With MyCTE as (
    select R
    From table1 t1
    CROSS APPLY (
        SELECT R = 'SELECT ' + CONVERT (NVARCHAR(MAX),t1.IdExpresion) + ' as IdExpresion,' + STRING_AGG ('REPLACE','(') + '(' + 't1.expresion,''' + STRING_AGG(t2.searchExpresion + ''',''' + t2.replacementExpresion , '''),''') + ''') as expresion FROM table1 t1 where t1.IdExpresion = ' + CONVERT (NVARCHAR(MAX),t1.IdExpresion)
        from table2 t2
        where t2.IdExpresion = t1.IdExpresion
    ) C
)
SELECT @SQLString = STRING_AGG(R,'
UNION ALL
')
FROM MyCTE
--PRINT @SQLString
EXECUTE sp_executesql @SQLString
GO
注意我建议执行一些测试,以确认这解决了所有情况

注意我正在使用添加到SQL Server 2017的函数
STRING_AGG
。在旧版本中,您可以使用
FOR XML
语句获得完全相同的解决方案

由于我们没有真正的DDL+DML,我们无法真正讨论性能,但解决方案的执行计划差异为10%到90%(通常,在选择解决方案之前,您还应该检查生产中的IO和时间统计信息)

所以。。。这是执行计划图像(上面的查询是我的动态SQL解决方案,下面是使用递归CTE=Loop的动态SQL解决方案)

早安

这是另一个解决方案。请检查这是否符合您的需要。此解决方案不使用任何循环,而是使用简单的动态查询

DECLARE @SQLString nvarchar(MAX);
-- do not make mistake, this is simple CTE and not a recursive CTE (no Loop)
;With MyCTE as (
    select R
    From table1 t1
    CROSS APPLY (
        SELECT R = 'SELECT ' + CONVERT (NVARCHAR(MAX),t1.IdExpresion) + ' as IdExpresion,' + STRING_AGG ('REPLACE','(') + '(' + 't1.expresion,''' + STRING_AGG(t2.searchExpresion + ''',''' + t2.replacementExpresion , '''),''') + ''') as expresion FROM table1 t1 where t1.IdExpresion = ' + CONVERT (NVARCHAR(MAX),t1.IdExpresion)
        from table2 t2
        where t2.IdExpresion = t1.IdExpresion
    ) C
)
SELECT @SQLString = STRING_AGG(R,'
UNION ALL
')
FROM MyCTE
--PRINT @SQLString
EXECUTE sp_executesql @SQLString
GO
注意我建议执行一些测试,以确认这解决了所有情况

注意我正在使用添加到SQL Server 2017的函数
STRING_AGG
。在旧版本中,您可以使用
FOR XML
语句获得完全相同的解决方案

由于我们没有真正的DDL+DML,我们无法真正讨论性能,但解决方案的执行计划差异为10%到90%(通常,在选择解决方案之前,您还应该检查生产中的IO和时间统计信息)

所以。。。这是执行计划图像(上面的查询是我的动态SQL解决方案,下面是使用递归CTE=Loop的动态SQL解决方案)


您可能能够摆脱
和CHARINDEX(t2.searchExpresion,cte.expresion)>0
。根据您在
#table1
中的行数,筛选器可能会比不运行
REPLACE()
时增加更多的开销。另外,如果使用“%”+t2.searchExpress+“%”之类的
cte.expression,它的性能可能会更高。
。Sql Server 2016 FAST的哪个版本?请参阅我的答案中的解决方案1。您可能可以摆脱
和CHARINDEX(t2.searchExpresion,cte.expresion)>0
。根据您在
#table1
中的行数,筛选器可能会比不运行
REPLACE()
时增加更多的开销。另外,如果使用“%”+t2.searchExpress+“%”之类的
cte.expression,它的性能可能会更高。
。Sql Server 2016 FAST的哪个版本?请参阅我的答案中的解决方案1。这主意不错,但没有理由对此请求使用任何循环。让我添加另一个解决方案,我猜它的性能会更好…好主意,但是没有理由对这个请求使用任何循环。让我添加另一个解决方案,我猜它的性能会更好…不是!我无法比较所有3种解决方案,因为对于使用递归CTE或动态SQL的解决方案,联合执行计划显示为0%,而XML解析显示为100%。因此,根据EPNot,我只比较了两个性能更好的查询!我无法比较所有3种解决方案,因为对于使用递归CTE或动态SQL的解决方案,联合执行计划显示为0%,而XML解析显示为100%。因此,根据EP+1 Brute force,我只比较了显示更好性能的两个查询。因此,基本上您将所有内容都放在一个varchar(max)中,替换该字符串中的所有内容,然后通过转换为XML转换返回到一个表中。嗯,我认为一个varchar(max)可以包含2GB。所以我想这必须是一个相当大的表才能达到