Sql server 如何使用SQL Server 2016中另一个表中的值替换长字符串中的模式子字符串
我在SQL Server 2016中遇到了替换函数的问题,我无法用任何模式替换。问题是: 我有一个大字符串,里面有两个查询,它们之间用@@@模式分隔,如下所示: 我有个问题@@你叫什么名字@@@你多大了@@@我需要 更多答案。@@您好@。如果您愿意,请回答 现在,@@@中的文本是一个查询。此查询的答案保存在另一个数据库表中 我想用表值替换文本中的查询。我们可以从表中运行游标来获取值,然后替换为特定位置。假设我们在表格(答案)中有如下查询值,我们可以按照查询顺序获取数据:Sql server 如何使用SQL Server 2016中另一个表中的值替换长字符串中的模式子字符串,sql-server,pattern-matching,Sql Server,Pattern Matching,我在SQL Server 2016中遇到了替换函数的问题,我无法用任何模式替换。问题是: 我有一个大字符串,里面有两个查询,它们之间用@@@模式分隔,如下所示: 我有个问题@@你叫什么名字@@@你多大了@@@我需要 更多答案。@@您好@。如果您愿意,请回答 现在,@@@中的文本是一个查询。此查询的答案保存在另一个数据库表中 我想用表值替换文本中的查询。我们可以从表中运行游标来获取值,然后替换为特定位置。假设我们在表格(答案)中有如下查询值,我们可以按照查询顺序获取数据: 你叫什么名字 你多大年
- 你叫什么名字
- 你多大年纪->29岁
- 你好吗->我很好
是否有人可以帮助我了解在哪一个SQL Server函数上可以获得按模式替换的功能,并使用表中的原始值替换查询?必须存在使用SQL游标的解决方案,但此问题不需要该解决方案。下面的解决方案使用两个(递归CTE)和一些。完整的解决方案似乎令人望而生畏,因此我将其背后的方法分为三个步骤 样本数据
declare @question table
(
question nvarchar(200) primary key clustered
);
insert into @question (question) values
('I have a question. @@What is your name@@@@what is your age@@I need more answers.@@How are you@@.Answer if you want.');
declare @answers table
(
key_ nvarchar(30) primary key clustered,
value_ nvarchar(30)
);
insert into @answers (key_, value_) values
('What is your name', 'Doe'),
('what is your age', '29'),
('How are you', 'I am fine');
-- 1. Sample Data
SET NOCOUNT ON;
IF OBJECT_ID('dbo.question','U') IS NOT NULL DROP TABLE dbo.question;
IF OBJECT_ID('dbo.answers','U') IS NOT NULL DROP TABLE dbo.answers;
CREATE TABLE dbo.question
(
qID int PRIMARY KEY CLUSTERED,
question nvarchar(200) NOT NULL
);
CREATE TABLE dbo.answers
(
aID int NOT NULL INDEX nc__answers__aID,
key_ nvarchar(30) NOT NULL,
value_ nvarchar(30) NOT NULL
);
insert into dbo.question (qID,question) values
(1,'I have a question. @@What is your name@@@@what is your age@@I need more answers.'+
'@@@@@@@@@@@@@@@@@@@How are you@@@@@@@@@@@@@@@@@@@@@@@@@@@.Answer if you want.'),
(2,'I have a question too.@@@@@@@@@@@@Your Name is?@@@how young are you?@@Answer This-'+
'@@@@@@@@@@@You good?@@@@@@@@@@@@@@@How is Bill@@@@@@@@Answer if you must!.');
insert into dbo.answers (aID,key_, value_) values
(1,'What is your name','Doe'),(1,'what is your age','29'),(1,'How are you','I am fine'),
(2,'Your Name is?','Fred'),(2,'how young are you?','38'),(2,'Answer This-','What?'),
(2,'You good?','Yeah!'),(2,'How is Bill','Sleepy');
-- 2. Add Many New rows for performance testing
DECLARE @multiplier BIGINT = 10000;
INSERT dbo.question
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))+2, q.question
FROM dbo.question AS q
CROSS JOIN core.rangeAB(1,@multiplier,1,1) AS r;
INSERT dbo.answers
SELECT a.aID%2+((a.aID+r.RN)*2), a.key_, a.value_
FROM dbo.answers AS a
CROSS JOIN core.rangeAB(1,@multiplier,1,0) AS r;
完整解决方案
with cte_position as
(
select charindex('@@', q.question, 0) as 'position',
1 as 'count_',
convert(nvarchar(30), null) as 'key_',
q.question
from @question q
union all
select charindex('@@', cp.question, cp.position+2),
cp.count_+1,
convert(nvarchar(30), case
when (cp.count_+1) % 2 = 0
then substring(cp.question, cp.position+2, charindex('@@', cp.question, cp.position+2)-cp.position-2)
else null
end),
cp.question
from cte_position cp
where charindex('@@', cp.question, cp.position+2) > 0
),
cte_replace as
(
select cp.count_,
cp.key_,
replace(cp.question, '@@'+cp.key_+'@@', a.value_) as 'newquestion'
from cte_position cp
join @answers a
on a.key_ = cp.key_
where cp.key_ is not null
and not exists ( select top 1 'x'
from cte_position cp2
where cp2.key_ is not null
and cp2.count_ < cp.count_ )
union all
select cp.count_,
cp.key_,
replace(cr.newquestion, '@@'+cp.key_+'@@', a.value_)
from cte_replace cr
join cte_position cp
on cp.count_ = cr.count_ + 2
join @answers a
on a.key_ = cp.key_
)
select top 1 cr.newquestion
from cte_replace cr
order by cr.count_ desc;
第2部分-用值替换键
重复CTE的结构CTE\u replace
:
cp.key\uu.不为空
@answer
执行实际操作,并将结果重命名为newquestion
cte_positions
并使用大于2的count_
对记录进行过滤(我们在步骤1中仅选择偶数行),获得下一个键@answer
执行实际的更换操作@answer
中找不到更多要替换的键时,递归停止
newquestion
-----------------------------------------------------------------------------
I have a question. Doe29I need more answers.I am fine.Answer if you want.
-- 3. Performance Test
BEGIN
PRINT CHAR(10)+'Start Test'+CHAR(10)+REPLICATE('-',90);
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF OBJECT_ID('tempdb..#t2') IS NOT NULL DROP TABLE #t2;
IF OBJECT_ID('tempdb..#t3') IS NOT NULL DROP TABLE #t3;
IF OBJECT_ID('tempdb..#t4') IS NOT NULL DROP TABLE #t4;
PRINT CHAR(10)+'delimitedSplit8K serial'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t1
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY dbo.delimitedSplit8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (MAXDOP 1);
PRINT CHAR(10)+'delimitedSplit8K parallel'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t2
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY dbo.delimitedSplit8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (QUERYTRACEON 8649);
PRINT CHAR(10)+'patExtract8K Serial'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t3
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY samd.patExtract8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (MAXDOP 1);
PRINT CHAR(10)+'patExtract8K Parallel'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t4
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY samd.patExtract8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (QUERYTRACEON 8649);
END
最简单的部分:从最后一个计数最高的结果集中选择记录,以便在按计数递减排序时第一个(top 1
)记录(cr.count\uu desc
)
结果
newquestion
-----------------------------------------------------------------------------
I have a question. Doe29I need more answers.I am fine.Answer if you want.
-- 3. Performance Test
BEGIN
PRINT CHAR(10)+'Start Test'+CHAR(10)+REPLICATE('-',90);
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF OBJECT_ID('tempdb..#t2') IS NOT NULL DROP TABLE #t2;
IF OBJECT_ID('tempdb..#t3') IS NOT NULL DROP TABLE #t3;
IF OBJECT_ID('tempdb..#t4') IS NOT NULL DROP TABLE #t4;
PRINT CHAR(10)+'delimitedSplit8K serial'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t1
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY dbo.delimitedSplit8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (MAXDOP 1);
PRINT CHAR(10)+'delimitedSplit8K parallel'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t2
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY dbo.delimitedSplit8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (QUERYTRACEON 8649);
PRINT CHAR(10)+'patExtract8K Serial'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t3
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY samd.patExtract8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (MAXDOP 1);
PRINT CHAR(10)+'patExtract8K Parallel'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t4
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY samd.patExtract8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (QUERYTRACEON 8649);
END
更新20200725 不需要游标、循环或递归CTE。游标或递归解决方案几乎肯定是缓慢且过于庞大的。一个基于集合的解决方案,利用或使用是最好的选择 注意更新的样本数据(包括更多@): 每个解决方案的结果:
FinalAnswer
--------------------------------------------------------------------------
I have a question. Doe29I need more answers.I am fine.Answer if you want.
delimitedSplit8K serial
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 7641 ms, elapsed time = 7643 ms.
delimitedSplit8K parallel
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 12454 ms, elapsed time = 2118 ms.
patExtract8K Serial
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 4172 ms, elapsed time = 4216 ms.
patExtract8K Parallel
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 7453 ms, elapsed time = 1343 ms.
与方法相比,提取器方法的优点是该方法“跳过”分隔符(@)。比较执行计划:
DSPLIT8K执行计划
patExtract8K执行计划
我在示例数据中添加了一些额外的分隔符,以显示它如何使用提取器提取6个必需项,并开始只向前处理6行(而不是55行)
注意,在本例中,我们只处理一条记录。为了更好地理解如何对多行执行此操作,您需要有更好的示例数据
最终编辑20200726(性能测试):
FinalAnswer
--------------------------------------------------------------------------
I have a question. Doe29I need more answers.I am fine.Answer if you want.
delimitedSplit8K serial
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 7641 ms, elapsed time = 7643 ms.
delimitedSplit8K parallel
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 12454 ms, elapsed time = 2118 ms.
patExtract8K Serial
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 4172 ms, elapsed time = 4216 ms.
patExtract8K Parallel
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 7453 ms, elapsed time = 1343 ms.
基于此线程中的讨论。我包括了两个不同的拆分器,可以用于此目的,并利用串行和并行执行计划对它们进行了测试
样本数据
declare @question table
(
question nvarchar(200) primary key clustered
);
insert into @question (question) values
('I have a question. @@What is your name@@@@what is your age@@I need more answers.@@How are you@@.Answer if you want.');
declare @answers table
(
key_ nvarchar(30) primary key clustered,
value_ nvarchar(30)
);
insert into @answers (key_, value_) values
('What is your name', 'Doe'),
('what is your age', '29'),
('How are you', 'I am fine');
-- 1. Sample Data
SET NOCOUNT ON;
IF OBJECT_ID('dbo.question','U') IS NOT NULL DROP TABLE dbo.question;
IF OBJECT_ID('dbo.answers','U') IS NOT NULL DROP TABLE dbo.answers;
CREATE TABLE dbo.question
(
qID int PRIMARY KEY CLUSTERED,
question nvarchar(200) NOT NULL
);
CREATE TABLE dbo.answers
(
aID int NOT NULL INDEX nc__answers__aID,
key_ nvarchar(30) NOT NULL,
value_ nvarchar(30) NOT NULL
);
insert into dbo.question (qID,question) values
(1,'I have a question. @@What is your name@@@@what is your age@@I need more answers.'+
'@@@@@@@@@@@@@@@@@@@How are you@@@@@@@@@@@@@@@@@@@@@@@@@@@.Answer if you want.'),
(2,'I have a question too.@@@@@@@@@@@@Your Name is?@@@how young are you?@@Answer This-'+
'@@@@@@@@@@@You good?@@@@@@@@@@@@@@@How is Bill@@@@@@@@Answer if you must!.');
insert into dbo.answers (aID,key_, value_) values
(1,'What is your name','Doe'),(1,'what is your age','29'),(1,'How are you','I am fine'),
(2,'Your Name is?','Fred'),(2,'how young are you?','38'),(2,'Answer This-','What?'),
(2,'You good?','Yeah!'),(2,'How is Bill','Sleepy');
-- 2. Add Many New rows for performance testing
DECLARE @multiplier BIGINT = 10000;
INSERT dbo.question
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))+2, q.question
FROM dbo.question AS q
CROSS JOIN core.rangeAB(1,@multiplier,1,1) AS r;
INSERT dbo.answers
SELECT a.aID%2+((a.aID+r.RN)*2), a.key_, a.value_
FROM dbo.answers AS a
CROSS JOIN core.rangeAB(1,@multiplier,1,0) AS r;
性能测试
newquestion
-----------------------------------------------------------------------------
I have a question. Doe29I need more answers.I am fine.Answer if you want.
-- 3. Performance Test
BEGIN
PRINT CHAR(10)+'Start Test'+CHAR(10)+REPLICATE('-',90);
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF OBJECT_ID('tempdb..#t2') IS NOT NULL DROP TABLE #t2;
IF OBJECT_ID('tempdb..#t3') IS NOT NULL DROP TABLE #t3;
IF OBJECT_ID('tempdb..#t4') IS NOT NULL DROP TABLE #t4;
PRINT CHAR(10)+'delimitedSplit8K serial'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t1
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY dbo.delimitedSplit8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (MAXDOP 1);
PRINT CHAR(10)+'delimitedSplit8K parallel'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t2
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY dbo.delimitedSplit8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (QUERYTRACEON 8649);
PRINT CHAR(10)+'patExtract8K Serial'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t3
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY samd.patExtract8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (MAXDOP 1);
PRINT CHAR(10)+'patExtract8K Parallel'+CHAR(10)+REPLICATE('-',90);
WITH q2 AS (SELECT q.qID FROM dbo.question AS q GROUP BY q.qID)
SELECT
q2.qID,
FinalAnswer = fa.FinalAnswer
INTO #t4
FROM q2
CROSS APPLY
(
SELECT ISNULL(a.value_,split.item)
FROM (SELECT q.* FROM dbo.question AS q WHERE q.qID = q2.qID) AS q
CROSS APPLY samd.patExtract8K(q.question,'@') AS split
LEFT JOIN (SELECT a.* FROM dbo.answers AS a WHERE a.aID = q2.qID) AS a
ON a.aID = q.qID
AND a.key_ = split.item
ORDER BY split.ItemNumber
FOR XML PATH('')
) AS fa(FinalAnswer)
OPTION (QUERYTRACEON 8649);
END
结果:
FinalAnswer
--------------------------------------------------------------------------
I have a question. Doe29I need more answers.I am fine.Answer if you want.
delimitedSplit8K serial
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 7641 ms, elapsed time = 7643 ms.
delimitedSplit8K parallel
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 12454 ms, elapsed time = 2118 ms.
patExtract8K Serial
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 4172 ms, elapsed time = 4216 ms.
patExtract8K Parallel
------------------------------------------------------------------------------------------
SQL Server Execution Times: CPU time = 7453 ms, elapsed time = 1343 ms.
为此,需要一个基于集合的解决方案。Patextract在这里工作是因为有超长的分隔符。我尝试了下面的递归CTE解决方案,但无法使其工作;部分原因是,逻辑需要活在一个比我有时间做的工作更多的函数中。尽管如此,对于(大多数情况下。)准备充分的答案,做得好。然而,递归会很慢,而且对于这种类型的事情来说有点复杂。性能完全取决于数据集的大小和复杂性以及可用的系统资源。如果没有更多的样本数据,很难验证。我没有为我的解决方案考虑预定义函数。@ AalbBistin对单行样本数据做了一些快速测试。如果我像您一样使用
主键(解决方案更新)定义表变量,那么我的查询计划的成本(00547064)大约是delimitedSplit8K
解决方案(00253554)的两倍。所以,如果函数是一个选项,那么您的解决方案将是首选方法。说得清楚,我喜欢您的解决方案,在某些方面,我喜欢递归CTES。我在上面添加了一个性能测试线束。我将不得不把你的逻辑转换成一个比计划的更耗时的函数。我怀疑rCTE解决方案会更慢-它肯定会产生更多IO。