2017-02-13 3 views
-2

У меня есть столбец VARCHAR, содержащий список разделенных запятыми чисел:Удалить значение из списка VarChar

listOfNumbers 
    1,2,3,4 
    11,1,1,3 
    4,2,1 
    1 

Теперь мне нужно создать функцию, которая принимает на входе номер и удалить, если из колонки.

Пример: если вход 1 должен быть результат:

listOfNumbers 
    2,3,4 
    11,3 
    4,2 
    empty 

Я попытался использовать мультипликатор ЗАМЕНЫ как:

declare @listOfNumbers as varchar(max) 
set @listOfNumbers = '1,2,3,1' 

select *, REPLACE(REPLACE(listOfNumbers,',1', ''),'1,', '') from 
(
select @listOfNumbers as listOfNumbers 
) t1 

Но это не правильно, так как я даже ЗАМЕНЫ числа, как 11 или 111.

Есть ли способ поймать все случаи?

Заранее благодарим за ответы.

Редактировать: Я знаю, что это неправильный способ обработки данных этого типа, но у меня нет выбора.

+2

Никогда, никогда не хранить данные в виде разделенных запятыми элементов. Это вызовет у вас массу проблем. – jarlh

+2

Почему вы не используете правильную модель в первую очередь? Храните их в отдельной таблице, которая связана с основной таблицей с помощью внешнего ключа –

+1

Как насчет поиска одной из популярных табличных функций (TVF), которые разделяют строку, разделенную запятыми, на столбец значений, Не хотите, а потом испортил остаток обратно во что-то злое? – HABO

ответ

0

Один из подходов состоит в том, чтобы преобразовать список, разделенный запятой, в XML, извлекать значения из XML, удалять назначенного жертвы и собирать все оставшееся.

-- Sample data. 
declare @ListsOfNumbers as Table (Id Int Identity, BadThing VarChar(256)); 
insert into @ListsOfNumbers values 
    ('1,2,3,4'), ('11,1,1,3'), ('4,2,1'), ('1'); 
select * from @ListsOfNumbers; 

-- Make a new mess. 
declare @ValueToDrop as Int = 1; 

-- Just to see what's going on: show everything. 
select * 
    from (
    select Id, BadThing, 
    Cast('<root><csv>' + Replace(BadThing, ',', '</csv><csv>') + '</csv></root>' as XML) as XMLBadThing 
    from @ListsOfNumbers) as PH cross apply 
     (select Val.value('.', 'integer') as Val 
      from XMLBadThing.nodes('root/csv') as SeparatedValues(Val)) as OtherPH; 

-- Just to see what's going on: without the specified target. 
select * 
    from (
    select Id, BadThing, 
    Cast('<root><csv>' + Replace(BadThing, ',', '</csv><csv>') + '</csv></root>' as XML) as XMLBadThing 
    from @ListsOfNumbers) as PH cross apply 
     (select Val.value('.', 'integer') as Val 
      from XMLBadThing.nodes('root/csv') as SeparatedValues(Val)) as OtherPH 
    where Val <> @ValueToDrop; 

-- Go from the sample data to regrouped stuff without the target value. 
with BadThings as (
    select Id, BadThing, XMLBadThing, Val 
    from (
    select Id, BadThing, 
    Cast('<root><csv>' + Replace(BadThing, ',', '</csv><csv>') + '</csv></root>' as XML) as XMLBadThing 
    from @ListsOfNumbers) as PH cross apply 
     (select Val.value('.', 'integer') as Val 
      from XMLBadThing.nodes('root/csv') as SeparatedValues(Val)) as OtherPH 
    where Val <> @ValueToDrop) 
    select distinct Id, NewBadThing 
    from BadThings as BT cross apply 
    (select Stuff((select ',' + Cast(Val as VarChar(10)) 
     from BadThings where Id = BT.Id order by Val for XML path(''), type).value('.[1]', 'VarChar(max)'), 1, 1, '') as NewBadThing) as X 

UPDATE

Слегка опрятнее версия окончательного запроса:

with BadThings as (-- @ListsOfNumbers with the comma-delimited list converted to an XML column. 
    select Id, BadThing, 
    Cast('<root><csv>' + Replace(BadThing, ',', '</csv><csv>') + '</csv></root>' as XML) as XMLBadThing 
    from @ListsOfNumbers), 
    BadThingValues as (-- BadThings with the XML parsed to produce one value per row. 
    select Id, BadThing, XMLBadThing, BadThingNodes.XMLNode.value('.', 'integer') as BadThingValue 
     from BadThings as BT cross apply 
     XMLBadThing.nodes('root/csv') as BadThingNodes(XMLNode)) 
-- select * from BadThings; -- To see the first step in the CTE use this SELECT . 
-- select * from BadThingValues; -- To see the second step in the CTE use this SELECT . 
    select distinct BTV.Id, BTV.BadThing, Coalesce(CDL.NewBadThing, '') as NewBadThing 
    from BadThingValues as BTV cross apply 
    (select Stuff((select ',' + Cast(BadThingValue as VarChar(10)) 
     from BadThingValues 
     where Id = BTV.Id and BadThingValue <> @ValueToDrop 
     for XML path(''), type).value('.[1]', 'VarChar(max)'), 1, 1, '') as NewBadThing) as CDL; 
+0

Это довольно блестяще, и он охватывает все случаи бахромы. Можете ли вы объяснить, почему вам нужно указать 'как SeparatedValues ​​(Val)'? – MindedSecurity

+0

@MindedSecurity Результат '.nodes' претендует на то, чтобы быть таблицей, возвращающей столбец узлов, отсюда и плохо названный' SeparatedValues ​​(Val) '. Я обновил ответ с несколько более аккуратной версией окончательного запроса, который имеет несколько более рациональные имена объектов. – HABO

1

Не стоит хранить значения в списках в строках.

Любое решение, которое вы решите решить, будет зависеть от того, являются ли строки правильными или нет.

Теперь простое решение, при условии, что у вас нет пробелов в ваших строк, и Seperator всегда «» является

select 
reverse(
    stuff(
     reverse(
      stuff(
       replace(
        ','+c+',' 
        ,','+cast(@i as nvarchar(max))+',' 
        ,',' 
        ) 
       ,1,1,'') 
       ) 
     ,1,1,'') 
    ) 
from 
(values 
    ('1,2,3,4') 
    ,('11,1,1,3') 
    ,('4,2,1') 
    ,('1') 
) a(c) 

Что я делаю, что я добавить «» в начале и конец каждой строки.

Тогда в замене «1,» (если вы хотите, чтобы удалить 1) с «»

Это оставляет строку с запятыми на обоих концах, которые вы не хотите, чтобы я удалить первую запятую используя «STUFF», затем я отменяю результат и удаляю первую запятую, используя «STUFF», затем меняю строку назад и заканчивая результатом

+0

Спасибо за ответ, я полагаю, что @i = 1 правильно? У меня все еще есть проблема с повторяющимися последовательными значениями в одной строке: удаляется только один. – MindedSecurity

0

Вот немного взлома вы можете играть с. Опять же, это не разумная идея хранить данные в этом формате, хотя вы, безусловно, могли бы найти это полезное вне области баз данных.

declare @list nvarchar(256) = '1, 2, 1, 3, 4'; 
declare @value_to_remove nvarchar(16) = '1'; 

select 
    replace(substring(
     replace(
      replace(
      ', ' + replace(@list, ',', ',,') + ',,,', 
      ', ' + @value_to_remove + ',', 
      '' 
     ), ',,', ','), 
     3, 256), ',,', '' 
    ) 

Я написал это, прежде чем даже увидеть другой ответ, но заметьте, что он обрабатывает соседние элементы списка. Трюк состоит в том, чтобы удвоить запятые и, конечно, включить окружающие запятые в поиск и заменить строки.

Возможно, эта альтернативная версия будет более четкой.Обратите внимание, что значения в списке не могут иметь никаких квадратных скобок.

select 
    replace(substring(
     replace(
      replace(
      '[ ' + replace(@list, ',', '][') + ']', 
      '[ ' + @value_to_remove + ']', 
      '' 
     ), '][', ','), 
     3, 256), ']', '' 
    ); 

Играть с как здесь: http://rextester.com/IHQ53263

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