2014-10-17 2 views
6

У меня есть столбец NVARCHAR(10) в таблице. Он может хранить любые строки UNICODE.SQL Server: Заменить символы, отличные от символа, в строке без временной таблицы

Я хочу заменить каждый символ, отличный от '1', с '0'.

Предположим, у меня есть строка 'C18 *'. Я должен получить «0100000100».

мне удалось сделать это с помощью вспомогательной таблицы, которая содержит индексы от 1 до размера моей колонки (10), как это:

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

Это работает, но это может быть сделано в лучше путь? Это кажется уродливым для простого обновления, игнорируя тот факт, что размер столбца может меняться со временем. Он также должен работать на SQL> = 2005.

SQL Скрипкиhere

Спасибо!

+0

Вы можете сделать это только с помощью стандартного прок или вы можете использовать процедуру .Net? Это что-то, что нужно запускать много раз или только один раз для очистки данных? –

+0

@MauriceReeves Он должен запускаться только один раз (некоторые поврежденные данные в нескольких наших базах данных) против десятков больших таблиц (10K + строк). Простое консольное приложение разрешило бы это, но я спросил, если такая же ситуация повторится + мне не нравится эта временная таблица. – darkdante

+2

Таблица чисел очень полезна. У многих это постоянный стол. –

ответ

5

Вот способ сделать это с помощью cte. В моей системе у меня на самом деле есть ctes в виде имени cteTally. Этот метод генерирует представление в 10 000 строк с нулевыми показаниями. ;) Ваш код, опубликованный, работает достаточно хорошо. В этом примере я переместил строку в таблицу, так как это то, с чем вы работаете в реальной системе.

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

Мне пришлось немного изменить свой код для работы с sql 2005. В 2008 году вы можете изменить это на значения (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) вместо всех выбранных. : D –

+0

Я только что создал свой номер с вашей техникой! Спасибо :) – darkdante

+0

Можете ли вы полагаться на порядок строк в подзапросе без явного ORDER BY? –

1

Если вы хотите обновить всю таблицу, может понадобиться 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 

Использование:

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

несколько иной подход, используя рекурсивный запрос:

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

Испытано в SQLFiddle

Смежные вопросы