2010-04-29 15 views
10

Wiem, że MS T-SQL nie obsługuje wyrażenia regularnego, ale potrzebuję podobnej funkcjonalności. Oto, co usiłuję zrobić:Zastąp symbolami wieloznacznymi w SQL

Mam pole tabeli varchar który przechowuje nawigacyjnego, tak:

/ID1: Kategoria1/ID2: Kategoria2/ID3: Category3/

Każda nazwa kategorii jest poprzedzona jej ID kategorii, oddzielone dwukropkiem. Chciałbym zaznaczyć i wyświetlić te okruszki ale chcę usunąć kategorii identyfikatory i dwukropek, tak:

/Kategoria1/Kategoria2/Category3/

Wszystko między wiodący ukośnik (/) do okrężnicy włącznie (:) powinno zostać usunięte.

Nie mam opcji wyodrębniania danych, manipulowania nimi na zewnątrz i ponownego wstawiania z powrotem do tabeli; więc staram się to osiągnąć w instrukcji SELECT.

Nie mogę również użyć kursora do przechodzenia przez każdy wiersz i czyszczenia każdego pola za pomocą zagnieżdżonej pętli, ze względu na liczbę wierszy zwróconych w SELECT.

Czy to można zrobić?

Dzięki wszystkim - Jay

+0

Co produktów i wersji bazy danych? – Thomas

+0

odebrano za pomocą prostego wybierania instrukcji za pomocą CTE, poniżej – Oliver

Odpowiedz

1

Dla SQL Server 2005+, można uzyskać regex wsparcia przez:

  1. Włączanie CLR (nie wymaga restartu instancji)
  2. Przesyłanie funkcjonalność CLR (w tym przypadku, regex wymień)

Korzystanie natywną TSQL, musisz zdefiniować REPLACE sprawozdań za wszystko chcesz usunąć:

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

Regex lub w inny sposób, musisz mieć pewność, że te wzory nie pojawiają się w rzeczywistych danych.

2

Możesz to zrobić za pomocą funkcji Split.Poniższa funkcja Podział opiera się na istnieniu tabeli numery, które dosłownie zawiera sekwencyjny wykaz numerów tak:

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 

wtedy może być w stanie uruchomić kwerendę jak tak gdzie strip prefiksy:

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

To daje wartości, takich jak:

Category1 
Category2 
Category3 

można wtedy użyć FOR XML PATH połączyć je ponownie:

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

MOŻLIWE sugestię, że tworzysz nowe pole do przechowywania żądanej wartości i używasz procesu takiego jak ten, który jest w stanie go wypełnić, a następnie zapełnisz wszystkie nowe rekordy dotyczące wprowadzania danych (i wszelkich zmian do oryginalnego pola), gdy zajmie to mniej czasu. Próba wykonania tego rodzaju procesu za każdym razem, gdy trzeba wybrać, jest bolesna. – HLGEM

+0

@HLGEM - Uzgodniono. Użyłbym powyższej funkcji do zrobienia jednorazowej populacji "oczyszczonej" kolumny nawigacyjnej. – Thomas

+0

Zgadzam się również, że byłoby to trochę uciążliwe za każdym razem, gdy uruchamiany jest SELECT. Jednak tworzenie, zapełnianie i utrzymywanie dodatkowej "oczyszczonej" kolumny nie było w tej chwili dla mnie opcją. Potrzebowałem alternatywnego podejścia. Bardzo doceniam wkład, niemniej jednak, Thomas. – Jay

4

Myślę, że najlepiej będzie użyć funkcji rekursywnej zdefiniowanej przez użytkownika (UDF). Zawarłem tu kod, którego można użyć do przekazania ciągu znaków w celu uzyskania pożądanych rezultatów.

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 

Następnie można wykonać go z:

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

To było najłatwiejsze do wdrożenia w naszym obecnym rozwiązaniu. Widziałem odniesienia do włączania CLR, jak sugerowali Josh i OMG Ponies, ale rekurencja po prostu mi nie przyszła. Bardzo doceniam wszystkie dane wejściowe, wszystko. – Jay

3

Działa to dla SQL Server 2005 iw górę.

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