从varchar列表中删除值

时间:2017-02-13 15:00:49

标签: sql tsql

我有一个包含逗号分隔数字列表的varchar列:

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

现在我需要创建一个输入数字的函数,并从列中删除。

示例:如果输入为1,则结果应为:

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

我尝试使用多个REPLACE,如:

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这样的数字。

有没有办法捕捉所有案件?

提前感谢您的回复。

编辑: 我知道这不是处理这类数据的正确方法,但我没有选择它。

3 个答案:

答案 0 :(得分:1)

将值存储在字符串列表中不是一个好主意。

您选择解决问题的任何解决方案都取决于字符串是否格式良好。

现在这是一个简单的解决方案,假设你的字符串中没有空格,而且分隔符始终是','是

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"删除第一个逗号,然后我反转结果,并使用&#删除第一个逗号34; STUFF",然后我将字符串反转,最后得到结果

答案 1 :(得分: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

<强>更新

最终查询的稍微整洁的版本:

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;

答案 2 :(得分: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