通过删除大括号中的文本来更新列

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

【讨论】:

不错的基于集合的解决方案,但您所做的工作比必要的要多。 还要注意,当有重复的行时,这会失败。

以上是关于通过删除大括号中的文本来更新列的主要内容,如果未能解决你的问题,请参考以下文章

通过删除尖括号来清理 html 安全吗?

iOS App显示中文输入法中的大括号

复制用大括号括起来的文本

如何通过排序命令按列大文件唯一?

从tkinter标签中删除花括号

查找大括号内的文本并替换包括大括号的文本