使用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
答案 0 :(得分:2)
要考虑的一些事情:
VARCHAR(MAX)
是存储XML
的不好的地方。它不能直接用于XML,也不适合特殊字符。最好使用真正的XML 或 - 如果有任何合理的理由将其保留为字符串 - 请使用NVARCHAR(MAX)
。 SQL Server在内部使用UTF16,您将浪费很多东西将VARCHAR转换为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