【问题标题】:Search characters in a String and replace it with a blank space T-SQL在字符串中搜索字符并将其替换为空格 T-SQL
【发布时间】:2019-04-05 08:28:07
【问题描述】:

我有一个包含无效字符列表的表格,例如:

InVCh
-----

!
"
$
%
&
'
(
)
*
+
,
.
/

然后,我有很多不同列数的表(所有这些列都是字符串类型),例如:

Product          Store
-------          ------
Prod1            Store1
Pr$od!2          Sto$re!2
P:;()ro!!!"d3    S:;()to!!!"re3

我想创建一个程序来查找所有这些无效字符并用空格替换它们,如果空格太多,那么我必须用一个空格替换它们。所以我的预期结果应该是:

Product          Store
-------          ------
Prod1            Store1
Pr od 2          Sto re 2
P ro d3          S to re3

这可能吗?

谢谢!

【问题讨论】:

  • 是的。使用while loop 查找和替换。
  • WHILE 循环会很慢,@Squirrel
  • 你有什么推荐的?
  • 虽然循环很慢...我们需要考虑每个表的百万条记录...
  • @GiuseppeLolli 你应该在问题和标签中提到这一点。

标签: sql sql-server tsql sql-server-2016


【解决方案1】:

由于是 SQL Server 2016,使用 R is an option。这似乎并不牵强,因为有一篇来自 2017 的 MSSQLTips 文章对此进行了描述:SQL Server 2016 Regular Expressions with the R Language

文章的代码也没有那么难:

create table dbo.tblRegEx (id int identity, a varchar(300), b  varchar(300) );

-- 3. Remove duplicate words
exec sp_execute_external_script @language=N'R'
, @script = N'
pattern <-"\\b(\\w+\\s*)(\\1\\s*)+";
inData$a <- gsub(pattern, "\\1", inData$a, perl = T );
outData <- inData;'
, @input_data_1 = N'select id, a, b from dbo.tblRegEx'
, @input_data_1_name = N'inData'
, @output_data_1_name=N'outData'
with result sets ( as object dbo.tblRegEx);

这个问题要求的东西要简单得多,只需替换一些字符即可。

create table #products 
(
    id int primary key identity, 
    product varchar(300), 
    store  varchar(300) 
);
go

insert into #products (product,store)
values 
('Prod1',            'Store1'),
('Pr$od!2',          'Sto$re!2'),
('P:;()ro!!!"d3',    'S:;()to!!!"re3')

exec sp_execute_external_script @language=N'R'
, @script = N'
pattern <-"[!\"$%&''()*+,./:;]+";
inData$product <- gsub(pattern, " ", inData$product, perl = T );
inData$store <- gsub(pattern, " ", inData$store, perl = T );
outData <- inData;'
, @input_data_1 = N'select id, product, store from #products'
, @input_data_1_name = N'inData'
, @output_data_1_name=N'outData'
with result sets ( as object #products);

与所有存储过程一样,结果只能返回给客户端,或用作INSERT INTO 的源。这可能是可用于更新源表的声明表或临时表或表变量:

declare @outData table (id int primary key, product varchar(300), store  varchar(300) );

insert into @outData
exec sp_execute_external_script @language=N'R'
, @script = N'
pattern <-"[!\"$%&''()*+,./:;]+";   
inData$product <- gsub(pattern, " ", inData$product, perl = T );
inData$store <- gsub(pattern, " ", inData$store, perl = T );
outData <- inData;'
, @input_data_1 = N'select id, product, store from #products'
, @input_data_1_name = N'inData'
, @output_data_1_name=N'outData' 



update #products
set product = r.product,
    store   = r.store
from #products inner join @outdata r on r.id=#products.id

select * from #products

这会返回:

id  product   store
--  -------   --------
1   Prod1     Store1
2   Pr od 2   Sto re 2
3   P ro d3   S to re3

【讨论】:

  • 这是一个很好的解决方案,但我认为最好将所有数据插入临时表,然后截断目标表,然后重新插入所有记录,以避免更新一个表中的数百万条记录。谢谢!
  • @GiuseppeLolli 在 2019 年之前的所有版本中,表变量本质上是一个临时表。如果您有那么多行,更好的解决方案是使用与目标表相同的永久临时表并使用partition switching 用另一张桌子“替换”一张桌子。这是一个元数据操作,因此几乎是即时的。
【解决方案2】:

如果没有该版本,我假设您可以访问最新的工具。因此,您可以使用FOR XML PATH 在需要替换的字符上创建一个字符串,然后使用TRANSLATE 将它们全部删除:

WITH C AS(
    SELECT *
    FROM (VALUES('!'),
                ('"'),
                ('$'),
                ('%'),
                ('&'),
                (''''),
                ('('),
                (')'),
                ('*'),
                ('+'),
                (','),
                ('.'),
                ('/'))V(InVCh)),
PS AS (
    SELECT *
    FROM (VALUES('Prod1','Store1'),
                ('Pr$od!2','Sto$re!2'),
                ('P:;()ro!!!"d3','S:;()to!!!"re3')) V(Product,Store))
SELECT REPLACE(TRANSLATE(PS.Product,V.C,REPLICATE(LEFT(V.C,1),LEN(V.C))),LEFT(V.C,1),'') AS Product,
        REPLACE(TRANSLATE(PS.Store,V.C,REPLICATE(LEFT(V.C,1),LEN(V.C))),LEFT(V.C,1),'') AS Store
FROM PS
     CROSS APPLY (VALUES((SELECT '' + InVCh
                          FROM C
                          FOR XML PATH(''),TYPE).value('.','varchar(MAX)')))V(C);

db<>fiddle

请注意,第 3 行的返回值是 'P:;rod3''S:;tore3',因为分号 (;) 或冒号 (:) 都不在要删除的字符列表中。您需要添加所有需要替换的字符。

似乎 OP 在 cmets 中提到他们正在使用 2016(为什么知道您使用的版本很重要!)。使用Ngrams8K 你可以这样做(虽然看起来很乱):

WITH C AS(
    SELECT *
    FROM (VALUES('!'),
                ('"'),
                ('$'),
                ('%'),
                ('&'),
                (''''),
                ('('),
                (')'),
                ('*'),
                ('+'),
                (','),
                ('.'),
                ('/'))V(InVCh)),
PS AS (
    SELECT *
    FROM (VALUES(1,'Prod1','Store1'),
                (2,'Pr$od!2','Sto$re!2'),
                (3,'P:;()ro!!!"d3','S:;()to!!!"re3')) V(ID,Product,Store))
SELECT PS.Product,V.Product,
       PS.Store,V.Store
FROM PS
     CROSS APPLY (VALUES((SELECT '' + N.token
                          FROM dbo.NGrams8k(PS.Product,1) N
                          WHERE NOT EXISTS (SELECT 1
                                            FROM C
                                            WHERE C.InVCh = N.token)
                          ORDER BY position
                          FOR XML PATH(''),TYPE).value('.','varchar(8000)'),
                         (SELECT '' + N.token
                          FROM dbo.NGrams8k(PS.Store,1) N
                          WHERE NOT EXISTS (SELECT 1
                                            FROM C
                                            WHERE C.InVCh = N.token)
                          ORDER BY position
                          FOR XML PATH(''),TYPE).value('.','varchar(8000)')))V(Product,Store)

【讨论】:

  • 听到为什么这被否决会很有趣。
猜你喜欢
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 2014-11-12
  • 2020-01-06
  • 2017-04-16
  • 2017-01-10
  • 2015-12-14
  • 1970-01-01
相关资源
最近更新 更多