2017-02-13 1 views
-2

Ich habe eine Varchar-Spalte eine Liste von Komma enthält Zahlen getrennt:Wert Entfernen von varchar Liste

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

Jetzt muß ich eine Funktion erstellen, die eine Zahl in einer Eingabe nimmt und entfernen, wenn aus der Säule.

Beispiel: Wenn der Eingang 1 ist das Ergebnis sollte sein:

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

Ich habe versucht, mehrere mit REPLACE wie:

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

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

Aber es nicht korrekt ist, da ich gerade Zahlen REPLACE wird wie 11 oder 111.

Gibt es eine Möglichkeit, alle Fälle zu erfassen?

Vielen Dank im Voraus für die Antworten.

Edit: Ich weiß, das ist nicht die richtige Art, um mit dieser Art von Daten umzugehen, aber ich habe keine Wahl darüber.

+2

Nie, nie speichern Daten als Komma getrennt Elemente. Es wird dir nur viel Ärger machen. – jarlh

+2

Warum verwenden Sie nicht das richtige Modell? Speichern Sie sie in einer separaten Tabelle, die über den Fremdschlüssel mit der Haupttabelle verknüpft ist. –

+1

Wenn Sie eine der beliebten Tabellenwertfunktionen (TVFs) finden, die eine durch Kommas getrennte Zeichenfolge in eine Spalte mit Werten aufteilen, können Sie den Wert, den Sie nicht verwenden, löschen nicht wollen, und dann den Rest in etwas Böses verderben? – HABO

Antwort

0

Ein Ansatz ist die kommaseparierte Liste zu konvertieren XML, extrahiere die Werte aus dem XML, lasse das designierte Opfer fallen und setze alles zusammen, was noch übrig ist.

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

Eine etwas aufgeräumter Version der letzten Abfrage:

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

Das ist ziemlich brilliant und deckt alle Randfälle ab. Können Sie erklären, warum Sie 'as SeparatedValues ​​(Val)' angeben müssen? – MindedSecurity

+0

@MindedSecurity Das Ergebnis von '.nodes' gibt vor, eine Tabelle zu sein, die eine Spalte von Knoten zurückgibt, daher der schlecht benannte' SeparatedValues ​​(Val) '. Ich habe die Antwort mit einer etwas aufgeräumteren Version der letzten Abfrage aktualisiert, die etwas rationellere Objektnamen hat. – HABO

1

Es ist keine gute Idee, Werte in Listen in Strings zu speichern.

Jede Lösung, die Sie zur Lösung Ihres Problems wählen, hängt davon ab, ob die Zeichenfolgen korrekt formatiert sind oder nicht.

nun eine einfache Lösung, vorausgesetzt, dass Sie keine Leerzeichen in den Saiten haben, und das seperator immer ist ‚‘ ist

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) 

Was ich tue, ist, dass ich ein Add ‚‘ an den Start und Ende jeder Zeichenfolge.

Dann ersetzen ‚1‘ mit (wenn Sie 1 entfernen möchten) ‚‘

Dies lässt eine Zeichenfolge mit Kommas an beiden Enden, die Sie nicht wollen, so entferne ich das erste Komma mit "STUFF", dann verwerfe ich das Ergebnis, und entfernen Sie das erste Komma mit "STUFF", dann kehre ich die Zeichenfolge zurück und am Ende mit dem Ergebnis

+0

Danke für die Antwort, ich nehme an, dass @i = 1 richtig? Ich habe immer noch ein Problem mit doppelten aufeinander folgenden Werten für die gleiche Zeichenfolge: nur eine wird entfernt. – MindedSecurity

0

Hier ist ein bisschen hacken können Sie mit spielen. Auch hier ist es sicherlich keine gute Idee, Daten in diesem Format zu speichern, obwohl Sie dies sicherlich außerhalb der Datenbank finden könnten.

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

Ich schrieb dies bereits vor der anderen Antwort, aber beachten Sie, dass es benachbarte Listenelemente behandelt. Der Trick besteht darin, die Kommas zu verdoppeln und natürlich die umgebenden Kommas in die Such- und Ersetzungszeichenfolgen einzufügen.

Sie könnten diese alternative Version klarer sein.Beachten Sie, dass die Werte in der Liste keine eckigen Klammerzeichen enthalten dürfen.

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

Wiedergabe mit beiden hier: http://rextester.com/IHQ53263