将字符串解析转换为XQuery和XPath

时间:2016-09-12 19:07:02

标签: xml xpath sql-server-2008-r2 xquery

使用SQL Server 2008 R2。我正在尝试转换一些非常差的XML数据,这些数据作为一个大字符串存储在列中。现在,我正在使用字符串解析例程从中提取一个值但是我想使用XPath和XQuery并且最终想要创建一个存储过程,只需要InstrumentID作为所需的输入,并将开始日期和结束日期作为可选项参数。

以下是我现在使用T-SQL的方式:

Use MyDb
DECLARE @First VARCHAR(15), @Second VARCHAR(15),
        @DateLow datetime, @DateHigh datetime, 
        @InstrumentID Varchar(15)

SET @First = '<InstrumentID>'
SET @Second = '</InstrumentID>'
SET @DateLow = '2003-04-01'  
SET @DateHigh = '2004-01-13' 
SET @InstrumentID = NULL
SET @InstrumentID = 'SB2093780001'

select * 
from
    (SELECT 
         out_interface_id, msg_id, 
         SUBSTRING(xml_msg, CHARINDEX(@First, xml_msg) + LEN(@First), 
         CHARINDEX(@Second, xml_msg) - 
         CHARINDEX(@First, xml_msg) - 
         LEN(@First)) as InstrumentID,
         msg_type, xml_msg, CAST(xml_msg AS XML) as [Quick_XML], 
         date_received,status, last_modified, environment,
         transaction_closed_date
     FROM
         MyTable
     WHERE
         msg_type IN ('ABC','DEF') 
         AND date_received >= @DateLow
         AND date_received < DATEADD(DAY, 1, @DateHigh)
    ) x
where 
    (x.InstrumentID = @InstrumentID or x.InstrumentID = NULL)
order by 
    date_received desc

这是我到目前为止尝试使用XPath和XQuery

Use MyDb
declare @x xml;

select
    out_interface_id, msg_id,
    CAST(xml_msg AS XML) as [Quick_XML],
    @x.query('//InstrumentID') AS InstrumentID,
    msg_type, xml_msg,
    date_received, status, last_modified, environment,
    transaction_closed_date
from 
    MyTable
where
    msg_type in ('ABC','DEF') 
order by 
    date_received desc

我知道@x.query无效,因为它不知道我想从xml_msg列中提取字段,但到目前为止我尝试过的所有内容都失败了。我想我已经接近了,但我一直在阅读我在XPath和XQuery上可以找到的所有内容,而且我找不到这个特定场景的任何内容。 xml_msg列是无类型的,可怕的xml,只是一个长字符串。也许在我查询它之前我必须清理它?在我将其转换为XML之后,该领域的一些摘录长达数百行。一如往常,任何帮助将不胜感激。

XML测试数据

xml_msg列是varchar(max)

<Proponix>
    <Header>
        <DestinationID>ABC</DestinationID>
        <SenderID>PRO</SenderID>
        <OperationOrganizationID>ABT1</OperationOrganizationID>
        <MessageType>ACCTV21</MessageType>
        <DateSent>20160701</DateSent>
        <TimeSent>1934</TimeSent>
        <MessageID>1091697493</MessageID>
    </Header>
        <Subheader>
            <OriginalInstrumentID>SB1499780001</OriginalInstrumentID>
                <SplitActivities>
                    <InstrumentID>SB1499780001</InstrumentID>
                </SplitActivities>
        </Subheader>
</Proponix>

您将在测试xml中看到有OriginalInstrumentID和InstrumentID。我只关心InstrumentID。 OriginalInstrumentID仅适用于msg_type字段中定义的某些类型的消息。

测试

select out_interface_id, msg_id,
CAST(xml_msg as XML).value('//InstrumentID[1]','nvarchar(MAX)') AS InstrumentID
from MyTable
order by date_received desc

当我尝试此代码时,出现以下错误:

  

Msg 2389,Level 16,State 1,Line 3   XQuery [value()]:'value()'需要单例(或空序列),找到类型为'xdt:untypedAtomic *'的操作数

我尝试了很多不同的东西,但没有任何工作。

最终工作查询

这只是一个括号问题。

select out_interface_id, msg_id,
    CAST(xml_msg as XML).value(
    '(//InstrumentID)[1]','nvarchar(MAX)')  AS InstrumentID
    ,msg_type, xml_msg, CAST(xml_msg AS XML) as [Quick_XML] 
    ,date_received,status, last_modified, environment
    ,transaction_closed_date
from MyTable
    where msg_type in ('ABC','DEF') 
    and date_received >= CAST(GETDATE() -1 as DATE)
    and date_received < CAST(GETDATE() as DATE)
order by date_received desc

1 个答案:

答案 0 :(得分:2)

要考虑的一些事情:

  • VARCHAR(MAX)是存储XML的不好的地方。它不能直接用于XML,也不适合特殊字符。最好使用真正的XML 或 - 如果有任何合理的理由将其保留为字符串 - 请使用NVARCHAR(MAX)。 SQL Server在内部使用UTF16,您将浪费很多东西将VARCHAR转换为XML ...
  • 使用XML,应尽可能具体。使用像XPath这样的(//InstrumentID)[1]很容易输入,但如果其他地方也有InstrumentID,则需要进行深度搜索并返回意外结果。

非常简单直接:

DECLARE @tbl TABLE(YourXMLColumn VARCHAR(MAX), OtherVAlue VARCHAR(MAX));
INSERT INTO @tbl VALUES
(
'<Proponix>
  <Header>
    <DestinationID>ABC</DestinationID>
    <SenderID>PRO</SenderID>
    <OperationOrganizationID>ABT1</OperationOrganizationID>
    <MessageType>ACCTV21</MessageType>
    <DateSent>20160701</DateSent>
    <TimeSent>1934</TimeSent>
    <MessageID>1091697493</MessageID>
  </Header>
  <Subheader>
    <OriginalInstrumentID>SB1499780001</OriginalInstrumentID>
    <SplitActivities>
      <InstrumentID>SB1499780001</InstrumentID>
    </SplitActivities>
  </Subheader>
</Proponix>','Some other values'
);
SELECT tbl.OtherVAlue
      ,CAST(tbl.YourXMLColumn AS XML).value('(//InstrumentID)[1]','nvarchar(max)') AS InstrumentID
FROM @tbl AS tbl 

可以从XML中获取任何其他值,就像上面一样简单。

我建议你使用最具体的路径,比如

SELECT tbl.OtherVAlue
      ,CAST(tbl.YourXMLColumn AS XML).value('(/Proponix/Subheader/SplitActivities/InstrumentID)[1]','nvarchar(max)') AS InstrumentID
FROM @tbl AS tbl 
相关问题