仅反转sql server中字符串的数字部分

时间:2015-06-02 09:20:30

标签: sql sql-server string tsql regexp-replace

使用T-SQL,我试图找到最简单的方法来反转字符串中的数字。所以像Test123Hello这样的字符串有Test321Hello

[Before]           [After]
Test123Hello       Test321Hello
Tt143 Hello        Tt341 Hello
12Hll              21Hll
Tt123H3451end      Tt321H1543end

6 个答案:

答案 0 :(得分:3)

你可以使用这个功能

    CREATE  FUNCTION [dbo].[fn_ReverseDigit_MA]
(
    @Str_IN nVARCHAR(max)   
)
RETURNS NVARCHAR(max)
AS
BEGIN   
    DECLARE @lenstr AS INT =LEN(@Str_IN)
    DECLARE @lastdigend AS INT=0


    while (@lastdigend<@lenstr)
    BEGIN

        DECLARE @strPart1 AS NVARCHAR(MAX)=LEFT(@Str_IN,@lastdigend)
        declare @lenstrPart1 AS INT=LEN(@strPart1)
        DECLARE @strPart2 AS NVARCHAR(MAX)=RIGHT(@Str_IN,@lenstr-@lastdigend)

        declare @digidx as int=patindex(N'%[0-9]%'  ,@strPart2)+@lenstrPart1
        IF(@digidx=@lenstrPart1)
        BEGIN
            BREAK;
        END     
        DECLARE @strStartdig AS NVARCHAR(MAX) = RIGHT(@Str_IN,@lenstr-@digidx+1)

        declare @NDidx as int=patindex(N'%[^0-9]%' ,@strStartdig)+@digidx-1
        IF(@NDidx<=@digidx)
        BEGIN
            SET @NDidx=@lenstr+1
        END
        DECLARE @strRet AS NVARCHAR(MAX)=LEFT(@Str_IN,@digidx-1) +REVERSE(SUBSTRING(@Str_IN,@digidx,@NDidx-@digidx)) +RIGHT(@Str_IN,@lenstr-@NDidx+1)
        SET @Str_IN=@strRet
        SET @lastdigend=@NDidx-1        
    END
    return @Str_IN  
END

答案 1 :(得分:2)

只需使用PATINDEX进行搜索,按部分追加结果字符串:

Sub StartsWithA()
    Cells(1, Columns.Count).End(xlToLeft).Offset(1, 1).Resize(Range("A" & Rows.Count).End(xlUp).Row - 1, 1) = "=IF(UPPER(LEFT(A2,1))=""A"",""pass"",""fail"")"
    Cells(1, Columns.Count).End(xlToLeft).Offset(0, 1).Formula = "Result"
    activesheet.calculate 'In case your calcs are set to manual
    Cells(1, Columns.Count).End(xlToLeft).Offset(0, 0).Resize(Range("A" & Rows.Count).End(xlUp).Row, 1).Copy
    Cells(1, Columns.Count).End(xlToLeft).Offset(0, 0).Resize(Range("A" & Rows.Count).End(xlUp).Row, 1).PasteSpecial xlPasteValues
    Application.CutCopyMode = False
End Sub

测试SQL

CREATE FUNCTION [dbo].[fn_ReverseDigits]
(
    @Value nvarchar(max)   
)
RETURNS NVARCHAR(max)
AS
BEGIN

    IF @Value IS NULL
        RETURN NULL

    DECLARE 
        @TextIndex int = PATINDEX('%[^0-9]%', @Value), 
        @NumIndex int = PATINDEX('%[0-9]%', @Value), 
        @ResultValue nvarchar(max)  = ''

    WHILE LEN(@ResultValue) < LEN(@Value)
    BEGIN

        -- Set the index to end of the string if the index is 0
        SELECT @TextIndex = CASE WHEN @TextIndex = 0 THEN LEN(@Value) + 1 ELSE LEN(@ResultValue) + @TextIndex END
        SELECT @NumIndex = CASE WHEN @NumIndex = 0 THEN LEN(@Value) + 1 ELSE LEN(@ResultValue) + @NumIndex END

        IF @NumIndex < @TextIndex
            SELECT @ResultValue = @ResultValue + REVERSE(SUBSTRING(@Value, @NumIndex, @TextIndex -@NumIndex))
        ELSE
            SELECT @ResultValue = @ResultValue + (SUBSTRING(@Value, @TextIndex, @NumIndex - @TextIndex))

        -- Update index variables
        SELECT
            @TextIndex = PATINDEX('%[^0-9]%', SUBSTRING(@Value, LEN(@ResultValue) + 1, LEN(@Value) - LEN(@ResultValue))), 
            @NumIndex = PATINDEX('%[0-9]%', SUBSTRING(@Value, LEN(@ResultValue) + 1, LEN(@Value) - LEN(@ResultValue)))

    END


    RETURN @ResultValue
END

结果

declare @Values table (Value varchar(20))
INSERT @Values VALUES
('Test123Hello'),
('Tt143 Hello'),
('12Hll'), 
('Tt123H3451end'),
(''),
(NULL)

SELECT Value, dbo.fn_ReverseDigits(Value) ReversedValue FROM @Values

答案 2 :(得分:1)

希望这有帮助:

declare @s nvarchar(128) ='Test321Hello'
declare @numStart as int, @numEnd as int
select @numStart =patindex('%[0-9]%',@s)
select @numEnd=len(@s)-patindex('%[0-9]%',REVERSE(@s))
select 
SUBSTRING(@s,0,@numstart)+
reverse(SUBSTRING(@s,@numstart,@numend-@numstart+2))+
SUBSTRING(@s,@numend+2,len(@s)-@numend)

答案 3 :(得分:1)

  

使用此功能,它也会处理多次出现的数字

create FUNCTION [dbo].[GetReverseNumberFromString] (@String VARCHAR(2000))
RETURNS VARCHAR(1000)
AS
BEGIN
    DECLARE @Count INT
    DECLARE @IntNumbers VARCHAR(1000)
    declare @returnstring varchar(max)=@String;
    SET @Count = 0
    SET @IntNumbers = ''

    WHILE @Count <= LEN(@String)
    BEGIN

        IF SUBSTRING(@String, @Count, 1) >= '0'
            AND SUBSTRING(@String, @Count, 1) <= '9'
        BEGIN
            SET @IntNumbers = @IntNumbers + SUBSTRING(@String, @Count, 1)

        END


        IF (
                SUBSTRING(@String, @Count + 1, 1) < '0'
                OR SUBSTRING(@String, @Count + 1, 1) > '9'
                )
            AND SUBSTRING(@String, @Count, 1) >= '0'
            AND SUBSTRING(@String, @Count, 1) <= '9'
        BEGIN

            SET @IntNumbers = @IntNumbers + ','
        END

        SET @Count = @Count + 1
    END
declare @RevStrings table (itemz varchar(50))

INSERT INTO @RevStrings(itemz)
select items from dbo.Split(@IntNumbers,',')

      select  @returnstring = Replace(@returnstring, itemz,REVERSE(itemz))from @RevStrings
    RETURN @returnstring
END

你的样本字符串

select  [dbo].[GetReverseNumberFromString]('Tt123H3451end')

结果

Tt321H1543end

更新:

如果你没有Split功能,那么首先创建它 我把它包括在下面

create FUNCTION Split
(    
      @Input NVARCHAR(MAX),
      @Character CHAR(1)
)
RETURNS @Output TABLE (
      Items NVARCHAR(1000)
)
AS
BEGIN
      DECLARE @StartIndex INT, @EndIndex INT

      SET @StartIndex = 1
      IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
      BEGIN
            SET @Input = @Input + @Character
      END

      WHILE CHARINDEX(@Character, @Input) > 0
      BEGIN
            SET @EndIndex = CHARINDEX(@Character, @Input)

            INSERT INTO @Output(Items)
            SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)

            SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
      END

      RETURN
END
GO

答案 4 :(得分:1)

这是一种基于集合的方法:

;WITH Tally (n) AS
(   
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n) 
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n) 
), UnpivotCTE AS (
    SELECT id, x.c, n, y.isNumber,
           n - ROW_NUMBER() OVER (PARTITION BY id, y.isNumber 
                                  ORDER BY n) AS grp
    FROM mytable
    CROSS JOIN Tally
    CROSS APPLY (SELECT SUBSTRING(col, n, 1)) AS x(c)
    CROSS APPLY (SELECT ISNUMERIC(x.c)) AS y(isNumber)
    WHERE n <= LEN(col) 
), ToConcatCTE AS (
   SELECT id, c, n, isNumber,         
          grp + MIN(n) OVER (PARTITION BY id, isNumber, grp) AS grpAsc
   FROM UnpivotCTE
)
SELECT id, col,
       REPLACE(
        (SELECT c AS [text()]
         FROM ToConcatCTE AS t
         WHERE t.id = m.id
         ORDER BY id, 
                  grpAsc, 
                  CASE WHEN isNumber = 0 THEN n END,
                  CASE WHEN isNumber = 1 THEN n END DESC
         FOR XML PATH('')), '&#x20;',' ') AS col2
FROM mytable AS m

使用计数表以便“忽略”&#39;字符串的所有字符。然后使用ROW_NUMBER来识别数字和非数字字符的孤岛。最后,FOR XML PATH用于重建具有反转的数字岛的初始字符串:ORDER BY用于按相反的顺序对数字字符的岛进行排序。

Fiddle Demo here

答案 5 :(得分:0)

这将执行您要求的特定字符串:

select 
substring('Test123Hello',1,4)
+
reverse(substring('Test123Hello',5,3))
+
substring('Test123Hello',8,5)

根据其余的值来判断,您需要为您获得的任何字母数字模式制作模板。例如,您可以将上述内容应用于具有以下形状的任何值:

select * from [B&A] where [before] like '[a-z][a-z][a-z][a-z][0-9][0-9][0-9]
[a-z][a-z][a-z][a-z][a-z]'

换句话说,如果您将值(之前和之后)放入表[B&amp; A]并在之前调用列&#39;和&#39;之后&#39;然后跑了这个:

select 
substring(before,1,4)
+
reverse(substring(before,5,3))
+
substring(before,8,5) as [after]
from [B&A] where [before] like '[a-z][a-z][a-z][a-z][0-9][0-9][0-9][a-z]
[a-z][a-z][a-z][a-z]'

然后它会给你&#39; Test321Hello&#39;。

但是除非您创建了类似的行,否则其他3行不会受到影响 &#39; [0-9] [A-Z]&#39;为每个字母数字形状键入模板,并将其应用于[B&amp; A]表。您必须将结果选择到临时表或另一个表中。

通过依次应用每个模板,您可以获得大部分内容,然后您必须查看有多少行未受影响,并检查字母数字形状是什么,并制作更多模板。最终你有一组代码,如果你运行它会捕获所有可能的组合。

您可以坐下来以这种方式设计代码,捕获[a-z]和[0-9]的所有可能组合。很大程度上取决于您正在处理的最大字符数。