在varchar字段中解析SQL Server xml字符串

时间:2009-10-09 22:50:14

标签: sql sql-server tsql xquery

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

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

<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

由此我假设我能够将此CAST返回到字符串然后执行一些instring或charindex函数来确定下划线的位置,以便我可以将所有这些封装在子字符串函数中以进行选择我需要的部分。没有太深入,我很确定我最终可以通过这种方式完成它,但我知道必须有一个更简单的方法。这种方式会在SQL语句中产生一个巨大的不可读字段,即使我将它移动到一个函数中,仍然会混淆,试图弄清楚发生了什么。

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

3 个答案:

答案 0 :(得分:5)

您可以使用XQuery - 只需将您的语句更改为:

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

它为你提供了一个VARCHAR(260)足够长的时间来保存任何有效的文件名和路径 - 现在你有一个字符串,可以使用SUBSTRING等工作。

马克

答案 1 :(得分:4)

直接的方法是使用SUBSTRING和CHARINDEX。假设(明智与否)文件名的第一部分不会改变长度,但你仍然想使用XQuery来定位文件名,这里有一个简短的repro,可以做你想要的:

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,但没有一个可能更高效或可读。要考虑的一种可能性是编写一个CLR例程,将常规表达式处理带入SQL。

顺便说一下,如果你的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;

答案 2 :(得分:1)

不幸的是,SQL Server并不是一个符合要求的XQuery实现 - 相反,它是XQuery规范草案版本的一个相当有限的子集。它不仅没有fn:substring-before,也没有fn:index-of使用fn:substring自己fn:string-to-codepoints,也没有{{1}}。所以,据我所知,你在这里遇到了SQL。