我有一个表,每行包含一个XML文件(列 XMLRow )。每个xml包含两部分, PartI 和 PartII 。对于每个partI,我们可以有多个PartII(SEQUENCE)。我想将它插入我的数据库。第一部分将插入TableI,PartII的第二个表称为TableII。
CREATE TABLE XMLDATA {
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
insertDate Datetime NOT NULL DEFAULT getdate(),
XMLRow XML NOT NULL
}
以下是XMLDATA表中的XMLRow示例,
<MessageFrame>
<messageNumber>20</messageNumber>
<value>
<BasicSafetyMessage>
<partI>
<msgCnt>127</msgCnt>
</partI>
<partII>
<SEQUENCE>
<partII-Id>0</partII-Id>
<partII-Value>BLUE</partII-Value>
</SEQUENCE>
<SEQUENCE>
<partII-Id>3</partII-Id>
<partII-Value>RED</partII-Value>
</SEQUENCE>
</partII>
</BasicSafetyMessage>
</value>
</MessageFrame>
创建如下表格,
CREATE TABLE TABLEI {
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
messageNumber INT NOT NULL,
msgCnt INT NOT NULL
};
GO
CREATE TABLE TABLEII {
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
T1_id Not NULL REFERENCES TableI (id) ON DELETE CASCADE,
partII-Id INT NOT NULL,
partII-Value NVARCHAR(50) NOT NULL
};
GO
我在每个表中都有Identity Primary Keys,因为数据库中存储的xml中没有唯一ID。
DECLARE @docHandle INT
DECLARE @XML AS XML
EXEC sp_xml_preparedocument @docHandle OUTPUT, @XML
SELECT @XML = XMLRow FROM [XMLDATA]
INSERT INTO TableI (messageId, msgCnt)
SELECT messageNumber, msgCnt
FROM OPENXML (@docHandle, '/MessageFrame/value/BasicSafetyMessage/partI')
WITH (
messageNumber int '../../../messageId',
msgCnt int 'msgCnt',
)
INSERT INTO TableII (T1_id, partII-Id, partII-Value)
SELECT @XML = XMLRow FROM [XMLDATA]
SELECT T1_id, partII-Id , partII-Value
FROM OPENXML (@docHandle, '/MessageFrame/value/BasicSafetyMessage/PartII/SEQUENCE')
WITH (
T1_id int @@IDENTITY,
partII-Id int 'partII-Id',
partII-Value int 'partII-Value',
)
EXEC sp_xml_removedocument @docHandle
结果应该是这样的,
**Table1**
------------------------
id messageNumber msgCnt
------------------------
1 20 127
**Table2**
-------------------------------
id T1_id partII-Id partII-Value
-------------------------------
1 1 0 BLUE
2 1 3 RED
第二个INSERT INTO返回NULL。有人可以帮我这个吗?我在这里做错了吗?
答案 0 :(得分:1)
好的。所以这将使你的XML变得扁平化:
DECLARE @XmlData XML = N'<MessageFrame>
<messageNumber>20</messageNumber>
<value>
<BasicSafetyMessage>
<partI>
<msgCnt>127</msgCnt>
</partI>
<partII>
<SEQUENCE>
<partII-Id>0</partII-Id>
<partII-Value>BLUE</partII-Value>
</SEQUENCE>
<SEQUENCE>
<partII-Id>3</partII-Id>
<partII-Value>RED</partII-Value>
</SEQUENCE>
</partII>
</BasicSafetyMessage>
</value>
</MessageFrame>';
SELECT
N1.MessageFrame.value('(messageNumber)[1]', 'INT') AS MessageNumber
, N3.partI.value('(msgCnt)[1]', 'INT') AS MessageCount
, N5.Sequence.value('(partII-Id)[1]', 'INT') AS Id
, N5.Sequence.value('(partII-Value)[1]', 'NVARCHAR(50)') AS Name
FROM @XmlData.nodes('MessageFrame') AS N1(MessageFrame)
CROSS APPLY N1.MessageFrame.nodes('value/BasicSafetyMessage') AS N2(MessageBasicSafety)
CROSS APPLY N2.MessageBasicSafety.nodes('partI') AS N3(partI)
CROSS APPLY N2.MessageBasicSafety.nodes('partII') AS N4(partII)
CROSS APPLY N4.partII.nodes('//partII/SEQUENCE') AS N5(Sequence);
并会给你以下结果
MessageNumber MessageCount Id Name
------------- ------------ -- ----
20 127 0 BLUE
20 127 3 RED
所以现在只需将所有内容放到具有正确引用的表中,您完全可以这样做。
答案 1 :(得分:1)
首先:FROM OPENXML
与相应的SP准备和删除文档已经过时,不应再使用了。而是使用适当的methods the XML data type provides。
使用此查询可以轻松地展平您的XML:
SELECT id
,XMLRow.value(N'(/MessageFrame/messageNumber/text())[1]',N'int')
,XMLRow.value(N'(/MessageFrame/value/BasicSafetyMessage/partI/msgCnt/text())[1]',N'int')
,p2.value(N'(partII-Id/text())[1]',N'int')
,p2.value(N'(partII-Value/text())[1]',N'nvarchar(max)')
FROM XMLDATA
CROSS APPLY XMLRow.nodes(N'/MessageFrame/value/BasicSafetyMessage/partII/SEQUENCE') AS A(p2);
但是以下内容将为您提供一种简单的方法来实时交叉引用 。为此我向TABLEI
添加了一列,希望对您有用:
CREATE TABLE XMLDATA (
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
insertDate Datetime NOT NULL DEFAULT getdate(),
XMLRow XML NOT NULL
);
CREATE TABLE TABLEI (
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
XMLDATA_ID INT, --I add this column, to use the OUTPUT clause with INSERT
--If this is no option for you, read this:
--https://stackoverflow.com/questions/10949730/is-it-possible-to-for-sql-output-clause-to-return-a-column-not-being-inserted
messageNumber INT NOT NULL,
msgCnt INT NOT NULL
);
CREATE TABLE TABLEII (
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
T1_id INT Not NULL REFERENCES TableI (id) ON DELETE CASCADE,
[partII-Id] INT NOT NULL,
[partII-Value] NVARCHAR(50) NOT NULL
);
GO
- 我插入两个XML来显示基于集合的原则:
INSERT INTO XMLDATA(XMLRow) VALUES
(N'<MessageFrame>
<messageNumber>20</messageNumber>
<value>
<BasicSafetyMessage>
<partI>
<msgCnt>127</msgCnt>
</partI>
<partII>
<SEQUENCE>
<partII-Id>0</partII-Id>
<partII-Value>BLUE</partII-Value>
</SEQUENCE>
<SEQUENCE>
<partII-Id>3</partII-Id>
<partII-Value>RED</partII-Value>
</SEQUENCE>
</partII>
</BasicSafetyMessage>
</value>
</MessageFrame>')
,(N'<MessageFrame>
<messageNumber>30</messageNumber>
<value>
<BasicSafetyMessage>
<partI>
<msgCnt>300</msgCnt>
</partI>
<partII>
<SEQUENCE>
<partII-Id>10</partII-Id>
<partII-Value>BLACK</partII-Value>
</SEQUENCE>
<SEQUENCE>
<partII-Id>20</partII-Id>
<partII-Value>WHITE</partII-Value>
</SEQUENCE>
</partII>
</BasicSafetyMessage>
</value>
</MessageFrame>');
- 我们需要一个临时表来获取基于IDENTITY的ID
DECLARE @tblGetInsertedId TABLE(XMLDATA_ID INT, Id INT);
- 我们使用OUTPUT子句来缓冲XMLDATA中的id和TABLEI中新给定的id:
INSERT INTO TableI(XMLDATA_ID, messageNumber, msgCnt)
OUTPUT inserted.XMLDATA_ID, inserted.id INTO @tblGetInsertedId(XMLDATA_ID, id)
SELECT id
,XMLRow.value(N'(/MessageFrame/messageNumber/text())[1]',N'int')
,XMLRow.value(N'(/MessageFrame/value/BasicSafetyMessage/partI/msgCnt/text())[1]',N'int')
FROM XMLDATA;
- 我们将其余部分插入TABLEII,加入id-puffer
INSERT INTO TABLEII(T1_id,[partII-Id], [partII-Value])
SELECT i.Id
,p2.value(N'(partII-Id/text())[1]',N'int')
,p2.value(N'(partII-Value/text())[1]',N'nvarchar(max)')
FROM XMLDATA xd
CROSS APPLY xd.XMLRow.nodes(N'/MessageFrame/value/BasicSafetyMessage/partII/SEQUENCE') AS A(p2)
LEFT JOIN @tblGetInsertedId i ON xd.id=i.XMLDATA_ID;
- 对结果感到满意:-D
SELECT * FROM TABLEI
SELECT * FROM TABLEII
- 清理(小心真实数据!)
GO
/*
DROP TABLE TABLEII;
DROP TABLE TABLEI;
DROP TABLE XMLDATA;
*/