0

我正在使用 Microsoft SQL Server 2014,并且正在尝试更新表中的某些列。

我想用另一个字符替换字符串的每个字符。

例如,这个词:

HELLO123

我想用 T 代替 H,用 Q 代替 E,用 Y 代替 L,用 I 代替 O,用 6 代替 1,用 7 代替 2,用 8 代替 3 等等。

我不确定 Microsoft SQL Server 2014 是否支持正则表达式,即创建一个函数并循环遍历每个字符并替换在具有数百万行的表上需要很长时间。

有没有人有任何像正则表达式一样工作并且可以快速的解决方案?

谢谢

4

3 回答 3

0

如果您没有注意到,那么问题REPLACE是您需要嵌套这些值,因为您正在嵌套类似 will REPLACE(REPLACE('HELLO','H','E'),'E','Q')return in 'QQLLO'not的东西'EQLLO'。正如评论中提到的,SQL Server 2017 引入了TRANSLATE,它只会处理一个字符一次,但是,因为你使用的是 2014,所以你不能使用它(TRANSLATE('HELLO','HE','EQ'))。

可以做的是创建一个查找表,然后将数据拆分为字符并重建它。对于大量数据,这不会很快,不,它不会变得更快;但它会“完成工作”:

--Create a table for the Cipher characters
CREATE TABLE dbo.CharCipher (InputChar char(1) NOT NULL,
                             OutputChar char(1) NOT NULL);
GO

--Add a Clustered Primary Key
ALTER TABLE dbo.CharCipher ADD CONSTRAINT PK_CharCipher PRIMARY KEY CLUSTERED (InputChar);
GO

--Ensure that the Output character us unique too    
CREATE UNIQUE NONCLUSTERED INDEX UX_CipherOutput ON dbo.CharCipher (OutputChar);
GO

--Add your Ciphers
INSERT INTO  dbo.CharCipher (InputChar,
                             OutputChar)
VALUES ('H','T'),
       ('E','Q'),
       ('L','Y'),
       ('O','I'),
       ('1','6'),
       ('2','7'),
       ('3','8');
GO

--Create a Sample table
CREATE TABLE dbo.YourTable (YourString varchar(15));
INSERT INTO dbo.YourTable (YourString)
VALUES('HELLO123');
GO

--And now the "Mess"... I mean solution
WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT TOP (8000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2, N N3, N N4)
SELECT YT.YourString,
       (SELECT ISNULL(CC.OutputChar,V.YourChar)
        FROM Tally T
             CROSS APPLY (VALUES(CONVERT(char(1),SUBSTRING(YT.YourString,T.I,1))))V(YourChar)
             LEFT JOIN dbo.CharCipher CC ON V.YourChar = CC.InputChar
        WHERE T.I <= LEN(YT.YourString)
        ORDER BY T.I
        FOR XML PATH(''),TYPE).value('.','varchar(8000)') AS NewString
FROM dbo.YourTable YT;

GO

--Clean up
DROP TABLE dbo.YourTable;
DROP TABLE dbo.CharCipher;
于 2019-12-05T16:40:01.830 回答
0
--this could work if the strings are all uppercase --<-- not true
--you could use nchar & foreign characters and handle everything correctly...it is just a pain to type...
declare @s varchar(20) = 'HELLO123';

--lower case everything
select @s = lower(@s)

--handle numbers with non printable characters  --> number to char() --> char() to new number
select @s = replace(replace(replace(replace(replace(replace(@s, '1', char(1)), '2', char(2)), '3', char(3)), char(1), '6'), char(2), '7'), char(3), '8')


--handle letters with case sensitive replacement (using a CS collation)
--(all letters are lowercased)lowercase letter --> new uppercase letter
select replace(replace(replace(replace(@s collate SQL_Latin1_General_CP1_CS_AS, 'h' , 'T'), 'e', 'Q'), 'l', 'Y'),  'o', 'I'); 



SELECT UPPER(REPLACE(REPLACE(LOWER('HELLO') collate SQL_Latin1_General_CP1_CS_AS, 'h','E'),'e','Q'));


GO

/*
SELECT dbo.shiftchars('0123456789::ABCDEFGHIJKLMNOPQRSTUVWXYZ::abcdefghijklmnopqrstuvwxyz');

just for fun *using a single replace()*
not for millions of rows
suitable for standard latin alphanumeric
extended ascii & non printable chars are not handled correctly.
*/

CREATE FUNCTION dbo.shiftchars(@s VARCHAR(8000))
RETURNS VARCHAR(8000)
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT
AS
BEGIN
        SELECT @s = REPLACE(@s, f collate SQL_Latin1_General_CP1_CS_AS , t)
        FROM
        (
        VALUES
            (1, '1', CHAR(1)),
            (1, '2', CHAR(2)),
            (1, '3', CHAR(3)),
            (1, '4', CHAR(4)),
            (1, '5', CHAR(5)),
            (1, '6', CHAR(6)),
            (1, '7', CHAR(7)),
            (1, '8', CHAR(8)),
            (1, '9', CHAR(9)),
            (1, '0', CHAR(254)),
            (1, 'A', CHAR(128)),
            (1, 'B', CHAR(129)),
            (1, 'C', CHAR(130)),
            (1, 'D', CHAR(131)),
            (1, 'E', CHAR(132)),
            (1, 'F', CHAR(133)),
            (1, 'G', CHAR(134)),
            (1, 'H', CHAR(135)),
            (1, 'I', CHAR(136)),
            (1, 'J', CHAR(137)),
            (1, 'K', CHAR(138)),
            (1, 'L', CHAR(139)),        
            (1, 'M', CHAR(140)),
            (1, 'N', CHAR(141)),
            (1, 'O', CHAR(142)),        
            (1, 'P', CHAR(143)),
            (1, 'Q', CHAR(144)),
            (1, 'R', CHAR(145)),        
            (1, 'S', CHAR(146)),
            (1, 'T', CHAR(147)),    
            (1, 'U', CHAR(148)),        
            (1, 'V', CHAR(149)),
            (1, 'W', CHAR(150)),
            (1, 'X', CHAR(151)),        
            (1, 'Y', CHAR(152)),
            (1, 'Z', CHAR(153)),
            (1, 'a', CHAR(154)),
            (1, 'b', CHAR(155)),
            (1, 'c', CHAR(156)),
            (1, 'd', CHAR(157)),
            (1, 'e', CHAR(158)),
            (1, 'f', CHAR(159)),
            (1, 'g', CHAR(160)),
            (1, 'h', CHAR(161)),
            (1, 'i', CHAR(162)),
            (1, 'j', CHAR(163)),
            (1, 'k', CHAR(164)),
            (1, 'l', CHAR(165)),        
            (1, 'm', CHAR(166)),
            (1, 'n', CHAR(167)),
            (1, 'o', CHAR(168)),        
            (1, 'p', CHAR(169)),
            (1, 'q', CHAR(170)),
            (1, 'r', CHAR(171)),        
            (1, 's', CHAR(172)),
            (1, 't', CHAR(173)),    
            (1, 'u', CHAR(174)),        
            (1, 'v', CHAR(175)),
            (1, 'w', CHAR(176)),
            (1, 'x', CHAR(177)),        
            (1, 'y', CHAR(178)),
            (1, 'z', CHAR(179)),
            --------------------
            (2, CHAR(1), '6'),
            (2, CHAR(2), '7'),
            (2, CHAR(3), '8'),
            (2, CHAR(4), '9'),
            (2, CHAR(5), '0'),
            (2, CHAR(6), '1'),
            (2, CHAR(7), '2'),
            (2, CHAR(8), '3'),
            (2, CHAR(9), '4'),
            (2, CHAR(254), '5'),
            (2, CHAR(128), 'M'),
            (2, CHAR(129), 'N'),
            (2, CHAR(130), 'O'),
            (2, CHAR(131), 'P'),
            (2, CHAR(132), 'Q'),
            (2, CHAR(133), 'R'),
            (2, CHAR(134), 'S'),
            (2, CHAR(135), 'T'),
            (2, CHAR(136), 'U'),
            (2, CHAR(137), 'V'),
            (2, CHAR(138), 'W'),
            (2, CHAR(139), 'X'),            
            (2, CHAR(140), 'Y'),
            (2, CHAR(141), 'Z'),
            (2, CHAR(142), 'A'),            
            (2, CHAR(143), 'B'),
            (2, CHAR(144), 'C'),
            (2, CHAR(145), 'D'),            
            (2, CHAR(146), 'E'),
            (2, CHAR(147), 'F'),        
            (2, CHAR(148), 'G'),            
            (2, CHAR(149), 'H'),
            (2, CHAR(150), 'I'),
            (2, CHAR(151), 'J'),            
            (2, CHAR(152), 'K'),
            (2, CHAR(153), 'L'),
            (2, CHAR(154), 'm'),
            (2, CHAR(155), 'n'),
            (2, CHAR(156), 'o'),
            (2, CHAR(157), 'p'),
            (2, CHAR(158), 'q'),
            (2, CHAR(159), 'r'),
            (2, CHAR(160), 's'),
            (2, CHAR(161), 't'),
            (2, CHAR(162), 'u'),
            (2, CHAR(163), 'v'),
            (2, CHAR(164), 'w'),
            (2, CHAR(165), 'x'),            
            (2, CHAR(166), 'y'),
            (2, CHAR(167), 'z'),
            (2, CHAR(168), 'a'),            
            (2, CHAR(169), 'b'),
            (2, CHAR(170), 'c'),
            (2, CHAR(171), 'd'),            
            (2, CHAR(172), 'e'),
            (2, CHAR(173), 'f'),        
            (2, CHAR(174), 'g'),            
            (2, CHAR(175), 'h'),
            (2, CHAR(176), 'i'),
            (2, CHAR(177), 'j'),            
            (2, CHAR(178), 'k'),
            (2, CHAR(179), 'l')                 
        ) AS v(o, f, t)
        ORDER BY o;

    RETURN (@s);

END
于 2019-12-05T18:43:54.923 回答
0

可以在 Sql Server 2014 上进行这样的争夺。
即使没有 UDF 或 CLR。

这是一种使用OUTER APPLYon aFOR XML展开和替换[0-9A-Za-z]范围字符的方法。

样本数据:

create table test 
(
  id int identity(1,1) primary key,
  col nvarchar(42)
);

insert into test (col) values
(N'HELLO 0123'),
(N'01234π56789'),
(N'abcdefghijklm>nopqrstuvwxyz'),
(N'ABCDEFGHIJKLM✓NOPQRSTUVWXYZ');

数量:

--
-- Temporary tally table with numbers
-- Will be used to unfold that characters
--
if object_id('tempdb..#nums') is not null
  drop table #nums;

create table #nums (n int primary key);

with rcte as
(
 select 1 n, max(len(col)) max_n
 from test
 union all 
 select n+1, max_n
 from rcte 
 where n <= max_n
)
insert #nums (n)
select n 
from rcte
option (maxrecursion 4000);

询问:

select t.*, a.scramble
from test t
outer apply
(
  select q.x.value('.','NVARCHAR(MAX)') as scramble
  from
  (
    select
     case 
     when substring(col,n,1) between N'0' and N'9'
     then substring(
      N'5678901234',charindex(substring(col,n,1),
      N'0123456789'),1)
     when unicode(substring(col,n,1)) between unicode(N'a') and unicode(N'z')
     then substring(
      N'nomrqputswvyzxiacbedghfjlk',charindex(substring(col,n,1),
      N'abcdefghijklmnopqrstuvwxyz'),1)
     when unicode(substring(col,n,1)) between unicode(N'A') and unicode(N'Z')
     then substring(
      N'NOMRQPUTSWVYZXIACBEDGHFJLK',charindex(substring(col,n,1),
      N'ABCDEFGHIJKLMNOPQRSTUVWXYZ'),1)
     else substring(col,n,1)
     end [text()]
    from #nums
    where n between 1 and len(col)
    order by n
    for xml path (''), type
  ) q(x)
  where q.x is not null
) a;

结果:

编号 | 上校 | 争夺                     
-: | :---------------------------- | :----------------------------
 1 | 你好 0123 | TQYYI 5678                   
 2 | 01234π56789 | 56789π01234                 
 3 | abcdefghijklm>nopqrstuvwxyz | nomrqputswvyz>xiacbedghfjlk  
 4 | ABCDEFGHIJKLM✓NOPQRSTUVWXYZ | NOMRQPUTSWVYZ✓XIACBEDGHFJLK

对db<>fiddle的测试在这里

--

更具体的解决方案VARCHAR

select t.*, a.scramble
from test t
outer apply
(
  select q.x.value('.', 'VARCHAR(MAX)') as scramble
  from
  (
    select
     case 
     when substring(col,n,1) between '0' and '9'
     then substring(
      '5678901234',charindex(substring(col,n,1),
      '0123456789'),1)
     when ascii(substring(col,n,1)) between ascii('a') and ascii('z')
     then substring(
      'nomrqputswvyzxiacbedghfjlk',charindex(substring(col,n,1),
      'abcdefghijklmnopqrstuvwxyz'),1)
     when ascii(substring(col,n,1)) between ascii('A') and ascii('Z')
     then substring(
      'NOMRQPUTSWVYZXIACBEDGHFJLK',charindex(substring(col,n,1),
      'ABCDEFGHIJKLMNOPQRSTUVWXYZ'),1)
     else substring(col,n,1)
     end
    from #nums
    where n between 1 and len(col)
    order by n
    for xml path (''), type
  ) q(x)
  where q.x is not null
) a;

或者,进行旋转争夺的解决方案:

select t.*, a.scramble
from test t
outer apply
(
  select q.x.value('.', 'VARCHAR(MAX)') as scramble
  from
  (
    select 
    case
    when substring(col,n,1) between '0' and '9'
    then char(ascii('0')+(ascii(substring(col,n,1))-ascii('0')+5)%10)
    when ascii(substring(col,n,1)) between ascii('a') and ascii('z')
    then char(ascii('a')+(ascii(substring(col,n,1))-ascii('a')+13)%26)
    when ascii(substring(col,n,1)) between ascii('A') and ascii('Z')
    then char(ascii('A')+(ascii(substring(col,n,1))-ascii('A')+13)%26)
    else substring(col,n,1)
    end
    from #nums
    where n between 1 and len(col)
    order by n
    for xml path (''), type
  ) q(x)
) a

对db<>fiddle的测试在这里

请注意,对于 Sql Server 2017+ 解决方案,aSTRING_SPLIT可以替换FOR XML. 但是话又说回来,人们可以简单地使用TRANSLATEthen。

例子:

UPDATE test
SET col = TRANSLATE(col, 
           '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' collate Latin1_General_CS_AS, 
           '5678901234nomrqputswvyzxiacbedghfjlkNOMRQPUTSWVYZXIACBEDGHFJLK');
于 2019-12-05T20:04:30.657 回答