2009-10-09 45 views
6

我在用于存储xml数据的表中有一个varchar列。是的,我知道有一个我应该使用的xml数据类型,但我认为这是在xml数据类型可用之前设置的,所以现在我必须使用varchar。 :)在varchar字段中解析SQL Server xml字符串

存储的数据看起来类似于以下内容:

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

我需要解析的文件名,以获得两个下划线在这种情况下将是“456”的数字。文件名的第一部分“不应该”改变长度,但中间数字会改变。我需要一个解决方案,如果第一部分的长度发生变化(你知道它会改变,因为“不应该改变”总是意味着它会改变)。

对于我现在所拥有的,我使用XQuery来提取文件名,因为我认为这可能比直接字符串操作更好。我将字符串转换为xml来执行此操作,但我不是XQuery专家,所以当然我遇到了问题。我找到了一个XQuery函数(substring-before),但无法使其正常工作(我甚至不确定该函数是否适用于SQL Server)。可能有一个XQuery函数很容易做到这一点,但如果有我不知道它。

所以,我从桌上的文件名类似于下面的查询:

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

从这个我认为我能够施放此回字符串,然后做一些instring或charindex函数来计算出下划线的位置,以便我可以将所有这些内容封装在子字符串函数中以挑选出我需要的部分。不用太过分,我很确定我最终可以这样做,但我知道必须有一个更简单的方法。这种方式会使SQL语句中的一个巨大的不可读的字段,即使我将它移动到一个函数中,仍然会混淆,试图找出发生了什么。

我敢肯定有一个比这更容易,因为它似乎是简单的字符串操作。也许有人可以指出我正确的方向。谢谢

+1

什么版本的SQL Server? – 2009-10-10 01:39:03

+0

对不起,直到现在我才看到这个评论。我们现在正在使用SQL Server 2008。 – Dusty 2009-10-12 20:53:00

回答

5

您可以使用XQuery此 - 只是改变你的语句:

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

这就给了你一个VARCHAR(260),长到足以容纳任何有效的文件名和路径 - 现在你有一个字符串,可以在其与SUBSTRING等

马克工作

+0

我很欣赏你的回应,但我能够在我的文章中使用.query替代.value查询。我一直在寻找解析出文件名的最佳方法。但是,现在我们讨论了这个问题,是使用.query还是.value的首选方法? – Dusty 2009-10-12 14:09:16

+1

'query()'返回一个完整的XDM结果树作为'XML'数据类型的实例; 'value()'要求您的查询仅返回一个XDM值,并将其转换为某种SQL类型。因此,一般情况下,如果实际需要返回XML文档或片段,或者至少需要一个节点集,则需要返回前者;对于后者,只需返回单个值即可。 – 2009-10-12 18:37:06

+0

谢谢。这就说得通了。虽然它没有给你任何观点,但我赞成你的评论。 :) – Dusty 2009-10-12 20:51:55

1

不幸的是,SQL Server不是一致的XQuery实现 - 相反,它是XQuery spec草稿版本的一个相当有限的子集。它不仅没有fn:substring-before,它也没有fn:index-of自己使用fn:substringfn:string-to-codepoints。所以,据我所知,你在这里遇到了SQL。

+0

+1谢谢,我担心SQL Server只有XQuery的有限子集。看起来像我将不得不使用SQL Server中的子字符串函数来做到这一点,就像我在想,并且像史蒂夫卡斯回答的那样。 – Dusty 2009-10-12 14:46:23

4

直接的方法是使用SUBSTRING和CHARINDEX。假设(聪明与否)的文件名的第一部分不改变长度,但你仍然要使用XQuery来查找文件名,这里是一个简短的摄制,你想要做什么:

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; 

有是使用其他字符串函数(如REPLACE和PARSENAME或REVERSE)的偷偷摸摸的解决方案,但没有一个可能更有效或可读。需要考虑的一种可能性是编写一个将正则表达式处理带入SQL的CLR例程。顺便说一下,如果你的xml总是这么简单,那么没有什么特别的理由可以让我看到使用XQuery。这里有两个查询会提取你想要的数字。如果你没有过额外的空白区域控制在你的XML字符串或覆盖文件名的第一部分将改变长度的可能性,二是更安全:

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它看起来像我将不得不做我认为我会使用SQL Server子字符串来解析它。感谢您的回应,并为我完成大部分工作。我想我会创建一个类似于你的第一篇文章的函数,但是在这种情况下,你发布的第二个代码示例可以工作,但我宁愿使用XQuery在进行字符串操作之前拔出文件名。再次感谢您的帮助,我会将其标记为答案。 – Dusty 2009-10-12 14:50:01