2009-10-09 16 views
6

Mam kolumnę varchar w tabeli, która służy do przechowywania danych XML. Tak, wiem, że istnieje typ danych xml, który powinienem używać, ale myślę, że to było ustawione zanim typ danych xml był dostępny, więc varchar jest tym, co muszę teraz użyć. :)SQL Server xml parsowanie ciągów w polu varchar

Dane przechowywane wygląda podobnie do następującego:

<xml filename="100100_456_484351864768.zip" 
    event_dt="10/5/2009 11:42:52 AM"> 
    <info user="TestUser" /> 
</xml> 

muszę przeanalizować nazwę, aby uzyskać cyfry między dwoma podkreśleniami który w tym przypadku będzie „456”. Pierwsza część nazwy pliku "nie powinna" zmieniać długości, ale środkowa liczba będzie. Potrzebuję rozwiązania, które zadziałałoby, gdyby pierwsza część zmieniła swoją długość (wiesz, że to się zmieni, ponieważ "nie należy zmieniać" zawsze wydaje się oznaczać, że się zmieni).

Za to, co mam na razie, używam XQuery, aby wyciągnąć nazwę pliku, ponieważ uznałem, że jest to lepsze niż prosta manipulacja ciągiem. Rzucam ten ciąg do xml, aby to zrobić, ale nie jestem ekspertem od XQuery, więc oczywiście mam problemy. Znalazłem funkcję dla XQuery (substring-before), ale nie byłem w stanie jej uruchomić (nie jestem nawet pewien, czy ta funkcja będzie działać z SQL Server). Być może istnieje funkcja XQuery, aby to zrobić łatwo, ale jeśli nie jestem tego świadomy.

Tak, mam nazwę pliku z tabeli z kwerendy podobny do następującego:

select CAST(parms as xml).query('data(/xml/@filename)') as p 
from Table1 

z tego, że sądzę, że byłbym w stanie oddać to z powrotem do łańcucha wtedy zrobić kilka instring lub charindex, aby dowiedzieć się, gdzie znajdują się podkreślenia, aby można je było zamknąć w funkcji podłańcuchowej, aby wybrać potrzebną część. Nie wdając się zbytnio w to, jestem pewien, że w końcu mogę to zrobić w ten sposób, ale wiem, że musi być łatwiejszy sposób. W ten sposób powstanie ogromne nieczytelne pole w Oświadczeniu SQL, które nawet jeśli przeniosę go do funkcji, nadal będzie mylące, aby spróbować dowiedzieć się, co się dzieje.

Jestem pewien, że jest łatwiejsze, ponieważ wydaje się, że jest to prosta operacja na łańcuchach. Być może ktoś wskaże mi właściwy kierunek. Dzięki

+1

Jaką wersję programu SQL Server? –

+0

Niestety, nie widzę tego komentarza do chwili obecnej. Używamy teraz SQL Server 2008. – Dusty

Odpowiedz

5

Można użyć XQuery do tego - po prostu zmienić swoje oświadczenie:

SELECT 
    CAST(parms as xml).value('(/xml/@filename)[1]', 'varchar(260)') as p 
FROM 
    dbo.Table1 

który daje VARCHAR (260) na tyle długo, aby pomieścić każdą poprawną nazwę pliku i ścieżkę - teraz masz łańcuch i mogą pracować na nim podciąg itp

Marc

+0

Doceniam twoją odpowiedź, ale udało mi się to uzyskać za pomocą zapytania w moim poście, używając .query zamiast .value. Szukałem najlepszego sposobu na zanalizowanie nazwy pliku, gdy ją otrzymam. Jednak teraz, gdy jesteśmy na temat, jest preferowaną metodą użycia .query lub .value? – Dusty

+1

'query()' zwraca całe drzewo wyników XDM jako instancję typu danych 'XML'; 'value()' wymaga, aby zapytanie zwróciło tylko jedną wartość XDM i konwertuje ją na pewien typ SQL. Zasadniczo chodzi o pierwsze, gdy faktycznie trzeba zwrócić dokument XML lub fragment, lub przynajmniej jeden zestaw węzłów, a drugi, gdy trzeba zwrócić tylko jedną wartość. –

+0

Dzięki. To ma sens. Mimo że nie daje ci żadnych punktów, przegłosowałem twój komentarz. :) – Dusty

1

Niestety, SQL Server nie jest zgodną implementacją XQuery - raczej jest dość ograniczonym podzbiorem wersji roboczej specyfikacji XQuery. Nie tylko nie ma ona fn:substring-before, nie ma też fn:index-of, aby zrobić to sam, używając fn:substring lub fn:string-to-codepoints. Tak więc, o ile mogę powiedzieć, utknąłeś tutaj z SQL.

+0

+1 Dzięki, bałam się, że SQL Server ma ograniczony podzbiór XQuery. Wygląda na to, że będę musiał użyć funkcji podciągu w SQL Serverze, aby zrobić to tak, jak myślałem i jak odpowiedział Steve Kass. – Dusty

4

Prostym sposobem na to jest napis SUBSTRING i CHARINDEX. Zakładając (mądre czy nie), że pierwsza część nazwy pliku nie zmienia długości, ale nadal chcesz używać XQuery do zlokalizowania pliku, oto krótki repro, że robi to, co chcesz:

declare @t table (
    parms varchar(max) 
); 
insert into @t values ('<xml filename="100100_456_484351864768.zip" event_dt="10/5/2009 11:42:52 AM"><info user="TestUser" /></xml>'); 

with T(fName) as (
    select cast(cast(parms as xml).query('data(/xml/@filename)') as varchar(100)) as p 
    from @t 
) 
    select 
    substring(fName,8,charindex('_',fName,8)-8) as myNum 
    from T; 

Nie są podstępnymi rozwiązaniami, które używają innych funkcji łańcuchowych, takich jak REPLACE i PARSENAME lub REVERSE, ale żadne nie może być bardziej wydajne lub czytelne. Jedną z możliwości rozważenia jest napisanie procedury CLR, która zapewnia regularne przetwarzanie wyrażeń w SQL.

Nawiasem mówiąc, jeśli twój xml jest zawsze taki prosty, nie ma żadnego szczególnego powodu, dla którego mógłbym w ogóle użyć XQuery. Oto dwa zapytania, które wyodrębnią żądaną liczbę.Drugi jest bezpieczniejszy, jeśli nie masz kontroli nad dodatkowym białej przestrzeni w ciągu XML lub nad możliwością, że pierwsza część nazwy pliku zmieni długość:

select 
    substring(parms,23,charindex('_',parms,23)-23) as myNum 
    from @t; 

    select 
    substring(parms,charindex('_',parms)+1,charindex('_',parms,charindex('_',parms)+1)-charindex('_',parms)-1) as myNum 
    from @t; 
+0

+1 Wygląda na to, że będę musiał zrobić to, o czym myślałam, że będę musiał użyć podłańcucha SQL Server, aby go przetworzyć. Doceniam twoją odpowiedź i wykonuję dla mnie większość pracy. Wydaje mi się, że zrobię funkcję, która robi coś podobnego do twojego pierwszego posta, ale w tej sytuacji drugi wysłany przez ciebie kod działałby, ale wolałbym użyć XQuery, by wyłuskać nazwę pliku przed wykonaniem manipulacji ciągiem. Jeszcze raz dziękuję za pomoc i zaznaczę to jako odpowiedź. – Dusty