根据模式更新和替换字符串值

时间:2018-05-01 07:28:03

标签: sql sql-server sql-server-2008

我正面临一个复杂的SQL更新案例,我无法弄清楚如何解决。

我有nvarchar(32)类型的列,其中包含" 952683174"等数字。我需要根据以下规则替换它的字符串:

  • 1替换为3
  • 2替换为5
  • 3替换为4
  • 4替换为1
  • 5替换为9
  • 6替换为8
  • 7替换为2
  • 8替换为3
  • 9替换为6

所以前面的列值" 952683174"将是" 695834321"在表上执行更新查询后。

4 个答案:

答案 0 :(得分:4)

编辑:这适用于SQL Server 2017及更高版本。

TRANSLATE功能。

select TRANSLATE(yourcolumn,'123456789','354198236') FROM yourtable;

Demo

答案 1 :(得分:4)

您可以先用另一个字符替换每个数字,然后将字符替换为相应的数字,如下所示:

DECLARE @number nvarchar(32) = '952683174';

SELECT
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
@number,
'1', 'A'),'2', 'B'),'3', 'C'),'4', 'D'),'5', 'E'),'6', 'F'),'7', 'G'),'8', 'H'),'9', 'I'),
'A', '3'),'B', '5'),'C', '4'),'D', '1'),'E', '9'),'F', '8'),'G', '2'),'H', '3'),'I', '6')

Demo

答案 2 :(得分:3)

较新版本的SQL Server有更好的解决方案 - 例如2017版本中引入的Translate内置函数。

但是,由于这是2008版本,您必须自己操纵字符串。

我建议的解决方案是使用表格进行翻译,与DhruvJoshi的答案(我认为我的答案更简单)不完全相同,但这是一种非常类似的方法。

话虽如此,这是另一种方法,根本不使用replace

首先,创建并填充样本表(在将来的问题中保存此步骤):

DECLARE @T AS TABLE
(
    Col nvarchar(32)
)

INSERT INTO @T (Col) VALUES 
('952683174'),
('123456789'),
('06432') -- added more values to make sure I didn't mess it up

然后,创建并填充转换表:

DECLARE @Translate AS TABLE
(
    original char(1),
    translation char(1)
)

INSERT INTO @Translate (original, translation) VALUES
('1', '3'),
('2', '5'),
('3', '4'),
('4', '1'),
('5', '9'),
('6', '8'),
('7', '2'),
('8', '3'),
('9', '6')

现在,使用堆叠的cte替换计数表(当然,如果你有一个实际的计数表,你可以使用它。如果没有,请阅读Jeff Moden的The "Numbers" or "Tally" Table: What it is and how it replaces a loop),cross apply并离开加入进行翻译,并for xml path作为string_agg(另一个函数最终在2017版本中构建),你可以这样做:

;WITH  E1(N) AS (SELECT 1 FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) V(v)), --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a CROSS JOIN E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a CROSS JOIN E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4)

SELECT  Col, 
        (
            SELECT ISNULL(translation, c)
            FROM @T as t1
            CROSS APPLY
            (
                SELECT Substring(Col, N, 1) As c, translation
                FROM cteTally
                LEFT JOIN @Translate ON Substring(Col, N, 1) = original
                WHERE N <= LEN(ISNULL(Col, ''))
            ) as t2
            WHERE t1.Col = t0.Col
            FOR XML PATH('')
        ) As translated
FROM @T t0

结果:

col         translated
952683174   695834321
123456789   354198236
06432       08145 -- Note that the 0 doesn't get translated...

You can see a live demo on rextester.

与目前提供的其他答案相比,我的解决方案的主要优势:

  1. 所有基于集合的方法,不使用替换或字符串操作。
  2. 所有翻译都存储为表值。没有什么是硬编码的。
  3. 翻译价值不限于一个字符。如果你决定翻译9到11,那只需要你改变翻译表(MatSnow的回答也有这个好处)

答案 3 :(得分:0)

您可以使用如下查询: See live demo 解释是在评论中内联

create table numbers (n nvarchar(32));
insert into numbers values ('952683174'),('5785348');
-- 1. We create ordering on numbers to retain original order
; with ordered_numbers
as
(
    select 
        n, 
        r= row_number() over(order by (SELECT NULL)) 
    from numbers
),
-- 2. we create a tally table to split out each digit position wise and replace with mapping in V(old,new)
cte as 
(
    select 
        newdigit= v.new,r
    from 
        ordered_numbers
    cross apply 
        (
            select 
            top (select LEN(n)) 
                row_number() over (order by (select null)) rn
            from
                sys.objects o1 cross join sys.objects o2)tally
            join 
            (
                values
                   ('1','3'),
                   ('2','5'),
                   ('3','4'),
                   ('4','1'),
                   ('5','9'),
                   ('6','8'),
                   ('7','2'),
                   ('8','3'),
                   ('9','6')
                )v(old,new)
    on  v.old=SUBSTRING(n,rn,1) 
 )
--3. We join back the digits into a single string using order created in step 1
SELECT n = REPLACE((
            select '|' + newdigit
            from cte c1
            where c1.r=c2.r
            FOR XML PATH('')
            ),'|','')
FROM cte c2
group by c2.r
order by c2.r asc