如果您了解如何正确Cascade APPLY,这将相当容易。注意示例数据和解决方案。
--==== Sample Data
DECLARE @table TABLE (ColA VARCHAR(100));
INSERT @table VALUES ('Ab,cde(123)'),('Ab,cde'),('yz,kol'),('yz,kol(567)'),('uv,xyz');
--==== Solution
SELECT
[Output] = CONCAT(SUBSTRING(f.String,f2.Pos+1,8000),', ',SUBSTRING(f.String,1,f2.Pos-1))
FROM @table AS t
CROSS APPLY (VALUES(ISNULL(NULLIF(CHARINDEX('(',t.ColA),0),LEN(t.ColA)+1))) AS st(Pos)
CROSS APPLY (VALUES(SUBSTRING(t.ColA,1,st.Pos-1))) AS f(String)
CROSS APPLY (VALUES(CHARINDEX(',',f.String))) AS f2(Pos);
返回:
Output
-----------------------
cde, Ab
cde, Ab
kol, yz
kol, yz
xyz, uv
更新 20210716
对于那些想知道为什么这个解决方案如此之快的人来说,这与APPLY 的魔力有关,在这种情况下Cascading APPLY。
首先让我们运行查询并比较执行计划:
APPLY 解决方案需要一个标量运算符来完成工作。另一种方式需要一个段和序列运算符来生成经过排序和过滤的 ROW_NUMBER。
请注意,计划中 26% 的 % 用于表变量。
性能测试:
--==== Sample Data
DECLARE @table TABLE (ColA VARCHAR(100));
INSERT @table VALUES ('Ab,cde(123)'),('Ab,cde'),('yz,kol'),('yz,kol(567)'),('uv,xyz');
;WITH cte
AS (
SELECT t.ColA AS [Column1]
,CASE
WHEN charindex('(', t.ColA) > 0
AND charindex(')', t.ColA) > charindex('(', t.ColA)
THEN stuff(t.ColA, charindex('(', t.ColA), charindex(')', t.ColA) - charindex('(', t.ColA) + 1, '')
ELSE t.ColA
END AS [Column2]
,0 AS [Level] FROM @table AS t
)
,cte1
AS (
SELECT *
,row_number() OVER (
PARTITION BY [Column1] ORDER BY [Level] DESC
) AS Rn
FROM cte
)
SELECT Column1, Column2 ,
REPLACE( REPLACE(cte1.Column2 , SUBSTRING(Column2 , 0 , CHARINDEX(N',' , Column2)) , N'') , N',' , N'') + N', ' + SUBSTRING(Column2 , 0 , CHARINDEX(N',' , Column2)) AS OutPut
FROM cte1 where Rn = 1
SELECT
[Output] = CONCAT(SUBSTRING(f.String,f2.Pos+1,8000),', ',SUBSTRING(f.String,1,f2.Pos-1))
FROM @table AS t
CROSS APPLY (VALUES(ISNULL(NULLIF(CHARINDEX('(',t.ColA),0),LEN(t.ColA)+1))) AS st(Pos)
CROSS APPLY (VALUES(SUBSTRING(t.ColA,1,st.Pos-1))) AS f(String)
CROSS APPLY (VALUES(CHARINDEX(',',f.String))) AS f2(Pos);
GO
-- Test Harness;
IF OBJECT_ID('tempdb..#table','U') IS NOT NULL DROP TABLE #table;
CREATE TABLE #table (ColA VARCHAR(100));
INSERT #table
SELECT TOP (1000000) f.X
FROM (VALUES(REPLACE(LEFT(NEWID(),12),'-',',')+'(xxx)'),
(REPLACE(LEFT(NEWID(),12),'-',','))) AS f(X)
CROSS JOIN sys.all_columns, sys.all_columns AS b;
--==== Run the test
DECLARE @x VARCHAR(100);
SET STATISTICS TIME ON;
;WITH cte
AS (
SELECT t.ColA AS [Column1]
,CASE
WHEN charindex('(', t.ColA) > 0
AND charindex(')', t.ColA) > charindex('(', t.ColA)
THEN stuff(t.ColA, charindex('(', t.ColA), charindex(')', t.ColA) - charindex('(', t.ColA) + 1, '')
ELSE t.ColA
END AS [Column2]
,0 AS [Level] FROM #table AS t
)
,cte1
AS (
SELECT *
,row_number() OVER (
PARTITION BY [Column1] ORDER BY [Level] DESC
) AS Rn
FROM cte
)
SELECT @x =
REPLACE( REPLACE(cte1.Column2 , SUBSTRING(Column2 , 0 , CHARINDEX(N',' , Column2)) , N'') , N',' , N'') + N', ' + SUBSTRING(Column2 , 0 , CHARINDEX(N',' , Column2))
FROM cte1
where Rn = 1
SELECT @x = CONCAT(SUBSTRING(f.String,f2.Pos+1,8000),', ',SUBSTRING(f.String,1,f2.Pos-1))
FROM #table AS t
CROSS APPLY (VALUES(ISNULL(NULLIF(CHARINDEX('(',t.ColA),0),LEN(t.ColA)+1))) AS st(Pos)
CROSS APPLY (VALUES(SUBSTRING(t.ColA,1,st.Pos-1))) AS f(String)
CROSS APPLY (VALUES(CHARINDEX(',',f.String))) AS f2(Pos)
SET STATISTICS TIME, IO OFF;
测试结果:
SQL Server Execution Times: CPU time = 3938 ms, elapsed time = 3935 ms.
SQL Server Execution Times: CPU time = 3422 ms, elapsed time = 3420 ms.
15% 的适度改进,我预计会更好,但 APPLY 解决方案显然更快。