通过删除大括号中的文本来更新列
Posted
技术标签:
【中文标题】通过删除大括号中的文本来更新列【英文标题】:Update column by removing text in braces 【发布时间】:2021-09-22 03:22:52 【问题描述】:我需要更新表中所有行的单列。更新应该以这样一种方式发生,即需要通过删除为字符串附加大括号的所有文本并交换由','
分隔的字符串来过滤掉数据。
例子:
Column A
---------
Ab,cde(123)
Ab,cde
yz,kol
yz,kol(567)
uv,xyz
first name,lastname (123456)
按照上面的例子,我需要最终的数据是这样的
Output
---------
cde, Ab
cde, Ab
kol, yz
kol, yz
xyz,uv
lastname,first name
请告诉我如何在 SQL Server 中实现上述输出。
【问题讨论】:
总是用逗号分隔的 2 个字符串吗?您是否尝试过使用charindex()
和 substring()
提取 2 字符串?
主要是它的 2 个字符串。在删除大括号并尝试更新 column 的所有数据之后,我还需要交换字符串。
您还需要向我们展示当它不是 2 个字符串时它是如何工作的
请停止像这样存储 CSV 数据。只需规范化您的表格并避免昂贵的字符串操作。
实际上,这是从 excel 文件中加载的。现在我们停止加载 excel 文件,需要清理旧数据。
【参考方案1】:
如果您了解如何正确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 解决方案显然更快。
【讨论】:
CROSS APPLY 是非常糟糕的解决方案。 带我,这是为什么呢? 对不起@DaleK 我的最后一条评论是针对 گلی。我更新了我的帖子以包含执行计划和性能测试。 伟大的工作......很高兴看到我们所知道的真实证据:)【参考方案2】:试试这个 SQL 代码:
;WITH cte
AS (
SELECT ColumnA AS [Column1]
,CASE
WHEN charindex('(', ColumnA) > 0
AND charindex(')', ColumnA) > charindex('(', ColumnA)
THEN stuff(ColumnA, charindex('(', ColumnA), charindex(')', ColumnA) - charindex('(', ColumnA) + 1, '')
ELSE ColumnA
END AS [Column2]
,0 AS [Level] FROM t1
)
,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
【讨论】:
不错的基于集合的解决方案,但您所做的工作比必要的要多。 还要注意,当有重复的行时,这会失败。以上是关于通过删除大括号中的文本来更新列的主要内容,如果未能解决你的问题,请参考以下文章