2009-10-09 14 views
6

Ho una colonna varchar in una tabella che viene utilizzata per memorizzare dati xml. Sì, so che c'è un tipo di dati xml che dovrei usare, ma penso che questo sia stato impostato prima che il tipo di dati xml fosse disponibile, quindi un varchar è ciò che devo usare per ora. :)SQL Server xml string parsing in campo varchar

I dati memorizzati sembra simile al seguente:

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

Ho bisogno di analizzare il nome del file per ottenere le cifre tra i due sottolineature che in questo caso sarebbe "456". La prima parte del nome del file "non dovrebbe" cambiare in lunghezza, ma il numero medio sarà. Ho bisogno di una soluzione che funzioni se la prima parte cambia in lunghezza (sai che cambierà perché "non dovrebbe cambiare" sembra sempre che significhi che cambierà).

Per quello che ho per ora, sto usando XQuery per estrarre il nome del file perché ho pensato che questo è probabilmente il meglio della manipolazione di stringhe diritte. Lancio la stringa su xml per farlo, ma non sono un esperto di XQuery, quindi ovviamente sto riscontrando problemi. Ho trovato una funzione per XQuery (sottostringa-prima), ma non riuscivo a farlo funzionare (non sono nemmeno sicuro che la funzione funzioni con SQL Server). Potrebbe esserci una funzione XQuery per farlo facilmente, ma se c'è ne sono inconsapevole.

Così, ottengo il nome del file dalla tabella con una query simile al seguente:

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

Da questo mi piacerebbe pensare che sarei stato in grado di lanciare questo ritorno a una stringa poi fare un po ' funzione di instring o charindex per capire dove sono i caratteri di sottolineatura in modo da poter incapsulare tutto ciò in una funzione di sottostringa per selezionare la parte di cui ho bisogno. Senza andare troppo oltre, sono abbastanza sicuro che alla fine riuscirò a farlo in questo modo, ma so che ci deve essere un modo più semplice. In questo modo si renderebbe un campo enorme illeggibile nell'istruzione SQL che, anche se lo trasferissi in una funzione, sarebbe comunque fonte di confusione per cercare di capire cosa sta succedendo.

Sono sicuro che esiste un modo più semplice poiché sembra essere una semplice manipolazione delle stringhe. Forse qualcuno può indicarmi la giusta direzione. Grazie

+1

Quale versione di SQL Server? –

+0

Siamo spiacenti, non ho visto questo commento fino ad ora. Stiamo utilizzando SQL Server 2008 ora. – Dusty

risposta

5

È possibile utilizzare XQuery per questo - solo modificare la dichiarazione:

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

che ti dà un VARCHAR (260) abbastanza a lungo per contenere qualsiasi nome di file valido e il percorso - ora avete una stringa e può lavorare su di essa con SUBSTRING ecc

Marc

+0

Apprezzo la tua risposta, ma sono riuscito a ottenere questo con la query nel mio post utilizzando .query anziché .value. Stavo cercando il modo migliore per analizzare il nome del file una volta ottenuto. Tuttavia, ora che siamo sull'argomento, è il metodo preferito per usare .query o .value? – Dusty

+1

'query()' restituisce un intero albero dei risultati XDM come istanza del tipo di dati 'XML'; 'value()' richiede che la query restituisca un solo valore XDM e la converte in un tipo SQL. Quindi in generale si va per il primo quando è effettivamente necessario restituire un documento o frammento XML, o almeno un nodeset, e per quest'ultimo quando è necessario restituire solo un singolo valore. –

+0

Grazie. Ciò ha senso. Anche se non ti dà alcun punto, ho upvoted il tuo commento. :) – Dusty

1

Sfortunatamente, SQL Server non è un'implementazione XQuery conforme, ma piuttosto un sottoinsieme piuttosto limitato di una versione bozza delle specifiche XQuery. Non solo non ha fn:substring-before, non ha nemmeno fn:index-of per farlo da solo usando fn:substring, né fn:string-to-codepoints. Quindi, per quanto posso dire, sei bloccato con SQL qui.

+0

+1 Grazie, temevo che SQL Server avesse un sottoinsieme limitato di XQuery. Sembra che dovrò utilizzare la funzione substring in SQL Server per farlo come stavo pensando e come ha risposto Steve Kass. – Dusty

4

Il modo semplice per farlo è con SUBSTRING e CHARINDEX. Supponendo (saggio o no) che la prima parte del nome del file non cambia la lunghezza, ma che si vuole ancora utilizzare XQuery per individuare il nome del file, ecco una breve Repro che fa ciò che si vuole:

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; 

Ci sono soluzioni subdole che utilizzano altre funzioni di stringa come REPLACE e PARSENAME o REVERSE, ma nessuna è probabilmente più efficiente o leggibile. Una possibilità da considerare è la scrittura di una routine CLR che porta la gestione di espressioni regolari in SQL.

A proposito, se il tuo xml è sempre così semplice, non c'è alcun motivo particolare per cui possa vedere di utilizzare XQuery. Ecco due query che estraggono il numero desiderato.Il secondo è più sicuro se non si ha il controllo su uno spazio bianco in più nella stringa di XML o per la possibilità che la prima parte del nome del file cambierà lunghezza:

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 Sembra che dovrò fare ciò che stavo pensando che sarebbe necessario utilizzare la sottostringa SQL Server per analizzarlo. Apprezzo la tua risposta e faccio la maggior parte del lavoro per me. Penso che farò una funzione che fa qualcosa di simile al tuo primo post, ma in questa situazione il secondo campione di codice che hai pubblicato funzionerebbe, ma preferirei usare XQuery per estrarre il nome del file prima di manipolare le stringhe. Grazie ancora per il tuo aiuto e segnerò questa come risposta. – Dusty