2014-10-17 19 views
6

Ich habe eine NVARCHAR(10) Spalte in einer Tabelle. Es kann alle Arten von UNICODE-Strings speichern.SQL Server: Ersetzen Sie Zeichen, die anders als ein Zeichen in einer Zeichenfolge sind, die keine temporäre Tabelle verwenden

Ich möchte jedes Zeichen, das sich von '1' unterscheidet, durch '0' ersetzen.

Nehmen wir an, ich habe die Zeichenfolge 'C18 *'. Ich sollte '0100000100' bekommen.

Ich schaffte es mit einem Helfer-Tabelle zu tun, die Indizes von 1 bis zur Größe meiner Spalte (10) enthält, wie folgt aus:

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,'') 

Es funktioniert, aber es kann in einem schöneren getan werden Weg? Dies scheint für ein einfaches Update hässlich zu sein und ignoriert die Tatsache, dass sich die Größe der Spalte im Laufe der Zeit ändern kann. Es muss auch auf SQL> = 2005 ausgeführt werden.

SQL Fiddle here

Dank!

+0

Können Sie dies nur tun, mit einem Standard-proc oder können Sie ein .Net-Verfahren verwenden? Ist das etwas, das oft oder nur einmal ausgeführt werden muss, um Daten zu scrubben? –

+0

@MauriceReeves Es muss nur einmal (einige korrupte Daten in einigen unserer Datenbanken) gegen Dutzende von großen Tabellen (10K + Zeilen) ausgeführt werden. Eine einfache Konsolen-App hätte das gelöst, aber ich habe gefragt, ob die gleiche Situation erneut auftritt. Ich mag diese temporäre Tabelle nicht. – darkdante

+2

Eine Zahlentabelle ist sehr nützlich. Viele haben es als festen Tisch. –

Antwort

5

Hier ist eine Möglichkeit, dies mit einem cte zu tun. In meinem System habe ich tatsächlich die ctes als Ansichtsname cteTally. Diese Technik erzeugt eine 10.000-Zeilen-Ansicht mit null Lesevorgängen. ;) Dein Code wie gepostet funktioniert ganz gut. In diesem Beispiel habe ich die Zeichenfolge in eine Tabelle verschoben, da Sie mit dem realen System arbeiten.

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

Ich hatte meine CTE ein wenig zu modifizieren mit SQL zu arbeiten 2005 2008+ Sie können, dass (1) auf Werte ändern, (1), (1), (1), (1), (1), (1), (1), (1), (1) anstelle aller Auswahlen. : D –

+0

Ich habe gerade meine Zahlenansicht mit Ihrer Technik erstellt! Danke :) – darkdante

+0

Können Sie sich auf die Reihenfolge der Zeilen in der Unterabfrage verlassen, ohne eine explizite ORDER BY? –

1

Wenn Sie eine ganze Tabelle aktualisieren möchten, könnte eine UDF nützlich sein.

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 

Verbrauch:

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

ein etwas anderer Ansatz, eine rekursive Abfrage:

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) ; 

Geprüft SQLFiddle