2014-10-17 20 views
6

Ho una colonna NVARCHAR(10) in una tabella. Può memorizzare qualsiasi tipo di stringhe UNICODE.SQL Server: sostituisce caratteri diversi da un carattere in una stringa senza alcuna tabella temporanea

Desidero sostituire ogni carattere diverso da "1" con "0".

Diciamo che ho la stringa 'C18 *'. Dovrei prendere "0100000100".

sono riuscito a farlo utilizzando una tabella di supporto che contiene gli indici da 1 alla dimensione della mia colonna (10), in questo modo:

CREATE TABLE HELP(Idx INT) 
INSERT INTO HELP 
    SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT  7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 

DECLARE @myStr VARCHAR(10) 
SET @myStr = 'C18*' 

SELECT STUFF((SELECT '' + CASE(B.Ch) WHEN '1' THEN '1' ELSE '0' END FROM (
    SELECT SUBSTRING(A.Val,H.Idx,1) AS Ch 
    FROM 
    (SELECT @myStr AS Val) A 
    CROSS JOIN HELP H 
)B FOR XML PATH('')),1,0,'') 

Funziona, ma può essere fatto in un posto più bello modo? Questo sembra brutto per un semplice aggiornamento, ignorando il fatto che le dimensioni della colonna possono cambiare nel tempo. Inoltre deve essere eseguito su SQL> = 2005.

SQL Fiddlehere

Grazie!

+0

si può fare solo con un proc di serie oppure puoi usare una procedura .Net? È qualcosa che deve essere eseguito molte volte o solo una volta per cancellare i dati? –

+0

@MauriceReeves Deve essere eseguito una sola volta (alcuni dati corrotti in un paio dei nostri database) contro dozzine di grandi tabelle (10K + righe). Una semplice console di console avrebbe risolto questo problema, ma ho chiesto nel caso in cui la stessa situazione si ripetesse + Non mi piace quella tabella temporanea. – darkdante

+2

Una tabella di numeri è molto utile. Molti lo hanno come tavolo permanente. –

risposta

5

Ecco un modo per farlo con un cte. Nel mio sistema ho effettivamente il ctes come nome di vista cteTally. Questa tecnica genera una vista di 10.000 righe con zero letture. ;) Il tuo codice come pubblicato funziona abbastanza bene. Per questo esempio ho spostato la stringa in una tabella poiché questo è ciò con cui stai lavorando nel sistema reale.

declare @myStrings table(MyVal varchar(10)); 

insert @myStrings 
select 'C18*'; 

WITH 
    E1(N) AS (select 1 from 
    (
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1) union all 
     select (1))dt(n)), 
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows 
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max 
    cteTally(N) AS 
    (
     SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 
    ) 

SELECT STUFF((SELECT '' + CASE(B.Ch) WHEN '1' THEN '1' ELSE '0' END FROM (
    SELECT SUBSTRING(A.MyVal, t.N, 1) AS Ch 
    FROM 
    @myStrings A 
    CROSS JOIN cteTally t 
    where t.N < LEN(a.MyVal) 
)B FOR XML PATH('')),1,0,'') 
+1

Ho dovuto modificare un po 'il mio cte per lavorare con sql 2005. Nel 2008+ puoi cambiarlo in valori (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) invece di tutti i selettivi. : D –

+0

Ho appena creato la visualizzazione dei numeri con la tua tecnica! Grazie :) – darkdante

+0

Puoi fare affidamento sull'ordine delle righe nella sottoquery senza un ORDER BY esplicito? –

1

Se si desidera aggiornare un'intera tabella, potrebbe essere utile una UDF.

Create FUNCTION dbo.F_MakeBinary(@Param NVarchar(max)) 
RETURNS NVarchar (max) 
AS 
BEGIN  
DECLARE @a NVarchar(max) 
Set @[email protected] 
While PATINDEX(N'%[^0-1]%', @a) > 0 
begin  
    select @a=STUFF(@a, PATINDEX(N'%[^0-1]%', @a),1,'0') 
end 
Return @a 
END 

Usage:

Update aTable Set aField = dbo.F_MakeBinary(aField) 
7

un approccio leggermente diverso, utilizzando una query ricorsiva:

WITH cte AS 
    (SELECT v, i = 0, 
     nv = CAST('' AS NVARCHAR(10)) 
    FROM t 
    UNION ALL 
    SELECT v, i+1, 
     CAST(nv + CASE WHEN SUBSTRING(v, i+1, 1) = '1' THEN '1' ELSE '0' END 
      AS NVARCHAR(10)) 
    FROM cte 
    WHERE i+1 <= LEN(v) 
) 
SELECT v, nv 
FROM cte 
WHERE i = LEN(v) ; 

Testato in SQLFiddle

Problemi correlati