2010-04-29 6 views
10

Ich weiß, dass MS T-SQL regulären Ausdruck nicht unterstützt, aber ich brauche ähnliche Funktionalität. Hier ist, was ich versuche zu tun:Ersetzen durch Platzhalter in SQL

ich ein varchar Tabellenfeld haben, die eine Brotkrümel speichert, wie folgt aus:

/ID1: Category1/ID2: Category2/ID3: Category3/

Jedem Kategorienamen ist seine Kategorie-ID vorangestellt, getrennt durch einen Doppelpunkt. Ich möchte diese Paniermehl auswählen und anzeigen, aber ich möchte die Kategorie IDs und Doppelpunkte entfernen, wie folgt aus:

/Category1/Category2/Category3/

Alles zwischen der führende Schrägstrich (/) bis einschließlich des Doppelpunktes (:) sollte ausgestrichen werden.

Ich habe nicht die Möglichkeit, die Daten zu extrahieren, extern zu manipulieren und wieder in die Tabelle einzufügen; also versuche ich dies in einer SELECT-Anweisung zu erreichen.

Ich kann auch nicht auf einen Cursor verwenden, um durch jede Zeile durchlaufen und jedes Feld mit einer verschachtelten Schleife wegen der Anzahl der Zeilen in der SELECT zurückgegeben.

Kann dies getan werden?

Dank all - Jay

+0

Welche Datenbank Produkt und Version? – Thomas

+0

beantwortet mit einfachen Select-Anweisung mit CTE, unter – Oliver

Antwort

1

Für SQL Server 2005+, können Sie durch regex Unterstützung erhalten:

  1. CLR Enabling (nicht Instanz Neustart erforderlich)
  2. Ihre CLR-Funktionalität Hochladen (in diesem Fall regex ersetzen)

nativen TSQL verwenden, müssen Sie REPLACE Anweisungen für alles definieren möchten Sie entfernen:

SELECT REPLACE(
     REPLACE(
      REPLACE(''/ID1:Category1/ID2:Category2/ID3:Category3/'', 'ID1:', ''), 
      'ID2:', ''), 
     'ID3:', '') 

Regex oder anders, Sie müssen sicherstellen, dass diese Muster nicht in den tatsächlichen Daten erscheinen.

2

Sie können dies möglicherweise mithilfe einer Split-Funktion tun.Die folgende Split-Funktion beruht auf der Existenz einer Numbers-Tabelle, die buchstäblich eine sequentielle Liste von Zahlen enthält etwa so:

Create Table dbo.Numbers(Value int not null primary key clustered) 
GO 
With Nums As 
    (
    Select ROW_NUMBER() OVER(Order By o.object_id) As Num 
    From sys.objects as o 
     cross join sys.objects as o2 
    ) 
Insert dbo.Numbers(Value) 
Select Num 
From Nums 
Where Num Between 1 And 10000 
GO 


Create Function [dbo].[udf_Split] (@DelimitedList nvarchar(max), @Delimiter nvarchar(2) = ',') 
Returns @SplitResults TABLE (Position int NOT NULL PRIMARY KEY, Value nvarchar(max)) 
AS 
/* 
PURPOSE: to split the @DelimitedList based on the @Delimter 
DESIGN NOTES: 
    1. In general the contents of the next item is: NextDelimiterPosition - CurrentStartPosition 
    2. CurrentStartPosition = 
     CharIndex(@Delimiter, A.list, N.Value) = Current Delimiter position 
     + Len(@Delimiter)      + The number of delimiter characters 
     + 1          + 1 since the text of the item starts after the delimiter 
    3. We need to calculate the delimiter length because the LEN function excludes trailing spaces. Thus 
     if a delimiter of ", " (a comma followed by a space) is used, the LEN function will return 1. 
    4. The DataLength function returns the number of bytes in the string. However, since we're using 
     an nvarchar for the delimiter, the number of bytes will double the number of characters. 
*/ 
Begin 
    Declare @DelimiterLength int 
    Set @DelimiterLength = DataLength(@Delimiter)/2 

    If Left(@DelimitedList, @DelimiterLength) <> @Delimiter 
     Set @DelimitedList = @Delimiter + @DelimitedList 

    If Right(@DelimitedList, @DelimiterLength) <> @Delimiter 
     Set @DelimitedList = @DelimitedList + @Delimiter 

    Insert @SplitResults(Position, Value) 
    Select CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength    
     , Substring (
        A.List 
        , CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength   
        , CharIndex(@Delimiter, A.list, N.Value + 1)        
         - (CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength) 
        ) 
    From dbo.Numbers As N 
     Cross Join (Select @DelimitedList As list) As A 
    Where N.Value > 0 
     And N.Value < LEN(A.list) 
     And Substring(A.list, N.Value, @DelimiterLength) = @Delimiter 
    Order By N.Value 

    Return 
End 

Sie könnten dann in der Lage sein, eine Abfrage wie so laufen, wo man die Präfixe Streifen aus:

Select Table, Substring(S.Value, CharIndex(':', S.Value) + 1, Len(S.Value)) 
From Table 
    Cross Apply dbo.udf_Split(Table.ListColumn, '/') As S 

Dies würde Ihnen Werte wie:

Category1 
Category2 
Category3 

Sie dann FOR XML PATH sie kombinieren wieder verwenden könnte:

Select Table.PK 
    , Stuff( (
       Select '/' + Substring(S.Value, CharIndex(':', S.Value) + 1, Len(S.Value)) 
       From Table As Table1 
        Cross Apply dbo.udf_Split(Table.ListColumn, '/') As S1 
       Where Table1.PK = Table.PK 
       Order By S1.Position 
       For Xml Path('') 
       ), 1, 1, '') As BreadCrumb 
From Table 
+1

MEIN Vorschlag ist, dass Sie ein neues Feld erstellen, um den gewünschten Wert zu speichern und verwenden Sie einen Prozess wie den, der sie füllen kann, dann füllen Sie alle neuen Datensätze auf Dateneingabe (und Änderungen) zum ursprünglichen Feld), wenn es weniger Zeit benötigen würde. Zu versuchen, diese Art von Prozess jedes Mal zu tun, wenn Sie auswählen müssen, ist schmerzhaft. – HLGEM

+0

@HLGEM - vereinbart. Ich würde die obige Funktionalität verwenden, um eine einmalige Population einer "gesäuberten" Brotkrumensäule zu machen. – Thomas

+0

Ich stimme auch zu, dies wäre jedes Mal ein bisschen beschwerlich, wenn ein SELECT ausgeführt wird. Das Erstellen, Füllen und Pflegen einer zusätzlichen "bereinigten" Spalte war für mich zur Zeit jedoch keine Option. Ich brauchte einen alternativen Ansatz. Ich schätze den Beitrag dennoch sehr, Thomas. – Jay

4

Ich denke, Ihre beste Wette wird sein, eine rekursive benutzerdefinierte Funktion (UDF) zu verwenden. Ich habe hier einen Code eingefügt, mit dem Sie eine Zeichenfolge übergeben können, um die gewünschten Ergebnisse zu erzielen.

CREATE FUNCTION ufn_StripIDsFromBreadcrumb (@cIndex int, @breadcrumb varchar(max), @theString varchar(max)) 

RETURNS varchar(max) 

AS 

BEGIN 
DECLARE @nextColon int 
DECLARE @nextSlash int 

SET @nextColon = CHARINDEX(':', @theString, @cIndex) 
SET @nextSlash = CHARINDEX('/', @theString, @nextColon) 
SET @breadcrumb = @breadcrumb + SUBSTRING(@theString, @nextColon + 1, @nextSlash - @nextColon) 

IF @nextSlash != LEN(@theString) 

    BEGIN 
    exec @breadcrumb = ufn_StripIDsFromBreadcrumb @cIndex = @nextSlash, @breadcrumb = @breadcrumb, @theString = @theString 
    END 
RETURN @breadcrumb 
END 

könnten Sie führen dann mit:

DECLARE @myString varchar(max) 
EXEC @myString = ufn_StripIDsFromBreadcrumb 1, '/', '/ID1:Category1/ID2:Category2/ID3:Category3/' 
PRINT @myString 
+0

Dies war am einfachsten in unsere aktuelle Lösung zu implementieren. Ich hatte Hinweise darauf gesehen, CLR zu aktivieren, wie Josh und OMG Ponies vorgeschlagen hatten, aber Rekursion kam mir einfach nicht in den Sinn. Ich schätze alle Eingaben sehr. – Jay

3

Dies ist für SQL Server 2005 und höher funktioniert.

create table strings (
    string varchar(1000) 
) 

insert into strings values('/ID1:Category1/ID2:Category2/ID3:Category3/') 
insert into strings values('/ID4:Category4/ID5:Category5/ID8:Category6/') 
insert into strings values('/ID7:Category7/ID8:Category8/ID9:Category9/') 
go 

with 
replace_with_wildcard (restrung) as 
( 
    select replace(string, '', '') 
    from strings 

    union all 

    select 
    replace(restrung, substring(restrung, patindex('%ID%', restrung), 4), '') 
    from replace_with_wildcard 
    where patindex('%ID%', restrung) > 0 
) 

select restrung 
from replace_with_wildcard 
where charindex(':', restrung) = 0 
order by restrung 

drop table strings 
0
declare @test1 nvarchar(max) 
set @test1='/ID1:Category1/ID2:Category2/ID3:Category3/' 
while(CHARINDEX('ID',@test1)<>0) 
Begin 
select @test1=REPLACE(@test1,SUBSTRING(@test1,CHARINDEX('ID',@test1),CHARINDEX(':',@test1)- 
CHARINDEX('ID',@test1)+1),'') 
End 
select @test1