2009-10-09 16 views
6

Tengo una columna varchar en una tabla que se utiliza para almacenar datos xml. Sí, sé que hay un tipo de datos xml que debería usar, pero creo que esto se configuró antes de que el tipo de datos xml estuviera disponible, por lo que debo usar un varchar por ahora. :)SQL Server xml cadena de análisis en el campo varchar

Los datos almacenados es similar a lo siguiente:

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

necesito para analizar el nombre del archivo para obtener los dígitos entre los dos guiones bajos que en este caso sería "456". La primera parte del nombre del archivo "no debería" cambiar de longitud, pero el número del medio lo hará. Necesito una solución que funcione si la primera parte cambia de longitud (usted sabe que cambiará porque "no debería cambiar" siempre parece indicar que cambiará).

Por lo que tengo por ahora, estoy usando XQuery para extraer el nombre de archivo porque calculé que esto es probablemente mejor que la manipulación directa de cadenas. Lancé la cadena a xml para hacer esto, pero no soy un experto en XQuery, por lo que estoy teniendo problemas. Encontré una función para XQuery (substring-before), pero no pude hacer que funcione (ni siquiera estoy seguro de que funcione con SQL Server). Puede haber una función XQuery para hacer esto fácilmente, pero si no lo sé, no lo sé.

Por lo tanto, me sale el nombre de archivo de la tabla con una consulta similar al siguiente:

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

De esta Me asumir que yo sería capaz de jugar esta vuelta a una cadena y luego hacer algunas Instring o Charindex funcionan para descubrir dónde están los caracteres de subrayado, de modo que pueda encapsular todo eso en una función de subcadena para seleccionar la parte que necesito. Sin ir demasiado lejos en esto, estoy bastante seguro de que eventualmente puedo hacerlo de esta manera, pero sé que tiene que haber una manera más fácil. De esta forma se convertiría en un gran campo ilegible en la declaración de SQL que, incluso si lo moviera a una función, sería confuso para tratar de descubrir qué está pasando.

Estoy seguro de que es más fácil que esto ya que parece ser una simple manipulación de cadenas. Quizás alguien pueda señalarme en la dirección correcta. Gracias

+1

¿Qué versión de SQL Server? –

+0

Disculpa, no vi este comentario hasta ahora. Estamos usando SQL Server 2008 ahora. – Dusty

Respuesta

5

Usted puede utilizar XQuery para este - sólo cambiar su estado de cuenta a:

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

Eso le da un VARCHAR (260) el tiempo suficiente para contener cualquier nombre de archivo válido y la ruta - ahora usted tiene una cadena y puede trabajar en él con SUBSECUENCIA etc.

Marc

+0

Agradezco su respuesta, pero pude obtener esto con la consulta en mi publicación utilizando .query en lugar de .value. Estaba buscando la mejor manera de analizar el nombre del archivo una vez que lo obtuve. Sin embargo, ahora que estamos en el tema, ¿es el método preferido para usar .query o .value? – Dusty

+1

'query()' devuelve un árbol de resultados de XDM completo como instancia del tipo de datos 'XML'; 'value()' requiere que su consulta devuelva un solo valor de XDM y lo convierta a algún tipo de SQL. Entonces, en general, opta por lo primero cuando realmente necesitas devolver un documento o fragmento XML, o al menos un conjunto de nodos, y para este último cuando necesitas devolver solo un valor. –

+0

Gracias. Eso tiene sentido. Aunque no le da ningún punto, elevé su comentario. :) – Dusty

1

Desafortunadamente, SQL Server no es una implementación de XQuery conforme, más bien, es un subconjunto bastante limitado de una versión borrador de la especificación de XQuery. No solo no tiene fn:substring-before, sino que tampoco tiene fn:index-of para hacerlo usted mismo usando fn:substring, ni fn:string-to-codepoints. Entonces, hasta donde puedo decir, estás atrapado con SQL aquí.

+0

+1 Gracias, temía que SQL Server tuviera un subconjunto limitado de XQuery. Parece que tendré que usar la función de subcadena en SQL Server para hacerlo como estaba pensando y como respondió Steve Kass. – Dusty

4

La manera directa de hacerlo es con SUBSTRING y CHARINDEX. Suponiendo (sabia o no) que la primera parte del nombre de archivo no cambia de longitud, pero que todavía quiere utilizar XQuery para localizar el nombre del archivo, he aquí una breve repro eso lo que quiere:

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; 

Hay son soluciones furtivas que usan otras funciones de cadenas como REEMPLAZAR y PARSENAME o REVERSE, pero es probable que ninguna sea más eficiente o legible. Una posibilidad para considerar es escribir una rutina CLR que traiga manejo de expresiones regulares a SQL.

Por cierto, si su xml es siempre así de simple, no hay ninguna razón en particular que pueda ver para usar XQuery en absoluto. Aquí hay dos consultas que extraerán el número que desee.La segunda es más seguro si usted no tiene control sobre el espacio en blanco extra en su cadena XML o sobre la posibilidad de que la primera parte del nombre del archivo cambiará longitud:

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 Parece que voy a tener que hacer lo que pensaba que tendría que hacer con la subcadena de SQL Server para analizarlo. Agradezco su respuesta y hago la mayor parte del trabajo por mí. Creo que haré una función que haga algo similar a tu primera publicación, pero en esta situación funcionaría la segunda muestra de código que publicaste, pero preferiría usar XQuery para arrancar el nombre del archivo antes de manipular la cadena. Gracias de nuevo por su ayuda y marcaré esto como la respuesta. – Dusty

Cuestiones relacionadas