2015-06-02 18 views
5

Con T-SQL, sto cercando di trovare il modo più semplice per invertire i numeri nella stringa. quindi per stringhe come Test123Hello hanno Test321Hello.Invertire solo le parti numeriche della stringa nel server sql

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

È questo solo per divertimento, o di un incarico stupido, o un problema di lavoro vero e proprio? – jarlh

+0

La parte numeri è invertita e la necessità di ripristinare nei miei dati – jozi

+0

Molto dipende dai parametri attorno alla stringa. Ci sono sempre parole prima e dopo i numeri e sono le parole della stessa lunghezza o di diverse lunghezze? – russ

risposta

2

Basta fare uso di PATINDEX per la ricerca, aggiungi alla stringa risultato parte per parte:

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 [email protected])) 
     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 

test SQL

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

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

Risultato

Value    ReversedValue 
-------------------- -------------------- 
Test123Hello   Test321Hello 
Tt143 Hello   Tt341 Hello 
12Hll    21Hll 
Tt123H3451end  Tt321H1543end 

NULL     NULL 
0

Ciò farebbe la stringa specifica si sta chiedendo:

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

giudicare dal resto dei valori sembra che si avrebbe bisogno di fare i modelli per uno dei modelli alfanumerici si stanno ottenendo . Per esempio si potrebbe applicare la sopra per i valori che aveva la forma:

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]' 

In altre parole, se si mettono i valori (prima e dopo) in una tabella [B & A] e chiamato le colonne 'prima 'e 'dopo' poi corse questo:

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]' 

allora sarebbe dare 'Test321Hello'.

Tuttavia gli altri 3 righe non sarebbero influenzati meno creato un simile '[0-9] [a-z]' modello tipo per ciascuna forma alfanumerica e applicato questo al [B & A] tavolo. Dovresti selezionare i risultati in una tabella temporanea o in un'altra tabella.

Applicando ciascun modello a turno si otterrebbe la maggior parte di esso quindi si dovrebbe vedere quante righe non sono state influenzate e verificare quale sia la forma alfanumerica e creare più modelli. Alla fine hai una serie di codici che, se corri, catturerebbero tutte le possibili combinazioni.

Si potrebbe semplicemente sedersi e progettare un codice in questo modo che ha catturato tutte le possibili combinazioni di [a-z] e [0-9]. Molto dipende dal numero massimo di personaggi con cui hai a che fare.

+0

Alla fine ti ritroverai con diversi schemi per ogni riga. Sospetto che l'OP voglia la soluzione universale –

+0

Sarei d'accordo nel guardare le quattro righe dato che ci vorranno quattro diversi modelli. Non so che i dati che sta ottenendo sono sempre diversi quindi ho pensato che gradualmente ogni schema si ripresentasse e quindi non ci sarebbe bisogno di essere un pattern per riga. Una soluzione universale sarebbe certamente carina. Sì. – russ

1

speranza questo aiuto:

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,@[email protected]+2))+ 
SUBSTRING(@s,@numend+2,len(@s)[email protected]) 
+0

è un lavoro per stringhe che hanno solo una sequenza numerica – ARZ

+0

con questo esempio la risposta non funziona "Tt123H3451end" – jozi

3

è possibile utilizzare questa funzione

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,@[email protected]) 

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

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

Usare questa funzione gestirà verificarsi multiplo di numeri troppo

create FUNCTION [dbo].[GetReverseNumberFromString] (@String VARCHAR(2000)) 
RETURNS VARCHAR(1000) 
AS 
BEGIN 
    DECLARE @Count INT 
    DECLARE @IntNumbers VARCHAR(1000) 
    declare @returnstring varchar(max)[email protected]; 
    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 

tua stringa di esempio

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

risultato

Tt321H1543end 

UPDATE:

se non si dispone di funzione split quindi prima creare lo ho inserito qui sotto

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 
+0

prova questo e fammi sapere se hai ancora problemi –

+0

Nome oggetto non valido 'dbo.Split'. – jozi

+0

ok in modo da non avere la funzione di split wait posso includerlo anche –

1

Questo è un approccio basato set:

;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 

Una tabella conteggio viene utilizzato al fine di 'UNPIVOT' tutto caratteri della stringa. Quindi viene utilizzato ROW_NUMBER per identificare isole di caratteri numerici e non numerici. Infine, FOR XML PATH viene utilizzato per ricostruire la stringa iniziale con isole numeriche invertite: ORDER BY viene utilizzato per ordinare isole di caratteri numerici in ordine inverso.

Fiddle Demo here

Problemi correlati