将 XML 数据插入 SQL 服务器中的多个表
Insert XML data into multiple tables in SQL Server
我有一个 XML 如下所示:
<Employees>
<Employee>
<AccountInfo>
<AccountNumber>1234567</AccountNumber>
<AccountType>Test</AccountType>
</AccountInfo>
<DocumentType>Test Doc</DocumentType>
<Date>12/01/2020</Date>
<Description>Test Description</Description>
<ImageFileType>pdf</ImageFileType>
<ImageFileName>321.PDF</ImageFileName>
<AdditionalInfo>
<FieldName>docDescription</FieldName>
<FieldValue>ABC XYZ</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Creation Date</FieldName>
<FieldValue>12/01/2020</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Department Code</FieldName>
<FieldValue>63</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>ID No</FieldName>
<FieldValue>3214567</FieldValue>
</AdditionalInfo>
</Employee>
</Employees>
我想将这个 XML 数据插入到 3 tables EmployeeInfo
、AccountInfo
和 AdditionalInfo
中,架构如下:
EmployeeInfo
(
EmployeeNumber Int Identity(1,1) NOT NULL,
DocumentType varchar(500) NULL,
[Description] varchar(500) NULL,
ImageFileName varchar(500) NULL,
ImageFileType varchar(500) NULL,
[Date] varchar(500) NULL
);
AccountInfo
(
EmployeeNumber int NOT NULL,
AccountNumber varchar(500) NULL,
AccountType varchar(500) NULL
);
AdditionalInfo
(
EmployeeNumber int NOT NULL,
FieldName varchar(500) NULL,
FieldValue varchar(500) NULL
);
EmployeeNumber
列用于 linking AccountInfo
和 AdditionalInfo
table 与 EmployeeInfo
.
AccountInfo
table 将在节点以下:
<AccountInfo>
<AccountNumber>1234567</AccountNumber>
<AccountType>Test</AccountType>
</AccountInfo>
AdditionalInfo
table 将获得这些 XML 节点:
<AdditionalInfo>
<FieldName>docDescription</FieldName>
<FieldValue>ABC XYZ</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Creation Date</FieldName>
<FieldValue>12/01/2020</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Department Code</FieldName>
<FieldValue>63</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>ID No</FieldName>
<FieldValue>3214567</FieldValue>
</AdditionalInfo>
其余xml节点被插入EmployeeInfo
.
我尝试使用此处显示的查询。我能够获取 xml 数据并将其插入主 table EmployeeInfo
,但无法使用身份 link AdditionalInfo
和 AccountInfo
在 EmployeeInfo
table.
中生成
注意:我在xml.
中有多个员工节点
DECLARE @EmpNumber int
DECLARE @x xml
SELECT @x = X FROM OPENROWSET (BULK 'C:\Test\Sample.xml', SINGLE_BLOB) AS EmpInfo(X)
DECLARE @hdoc int
EXEC sp_xml_preparedocument @hdoc OUTPUT, @x
INSERT INTO EmployeeInfo (DocumentType, [Description], ImageFileName, ImageFileType, [Date])
SELECT * FROM OPENXML (@hdoc, '/Employees/Employee', 2)
WITH ( DocumentType varchar(500), [Description] varchar(500), ImageFileName varchar(500), ImageFileType varchar(500), [Date] varchar(500))
SELECT @EmpNumber=SCOPE_IDENTITY()
INSERT INTO AccountInfo ([EmployeeNumber],[AccountNumber], [AccountType])
SELECT @EmpNumber, *
FROM OPENXML (@hdoc, '/Employees/Employee/AccountInfo', 2)
WITH (AccountNumber varchar(500), AccountType varchar(500))
INSERT INTO AdditionalInfo ([EmployeeNumber],[FieldName], [FieldValue])
SELECT @EmpNumber, *
FROM OPENXML (@hdoc, '/Employees/Employee/AdditionalInfo', 2)
WITH (
FieldName varchar(5000), FieldValue varchar(5000)
)
EXEC sp_xml_removedocument @hdoc
有人可以帮我解决这个问题吗?提前致谢。
避免使用 sp_xml_preparedocument、OPENXML 和 sp_xml_removedocument,因为它们效率低下,经常在忘记 sp_xml_removedocument 时导致资源泄漏,并鼓励 RBAR-like 在应该的地方构造成为 set-based RDBMS。
尽可能使用 nodes() and value(),例如以下...
insert dbo.EmployeeInfo (DocumentType, [Description], ImageFileName, ImageFileType, [Date])
select
Employee.value('(DocumentType/text())[1]', 'varchar(500)'),
Employee.value('(Description/text())[1]', 'varchar(500)'),
Employee.value('(ImageFileName/text())[1]', 'varchar(500)'),
Employee.value('(ImageFileType/text())[1]', 'varchar(500)'),
Employee.value('(Date/text())[1]', 'varchar(500)')
from @xml.nodes('/Employees/Employee') root(Employee);
declare @EmpNumber int = SCOPE_IDENTITY();
insert dbo.AccountInfo ([EmployeeNumber], [AccountNumber], [AccountType])
select
@EmpNumber,
AccountInfo.value('(AccountNumber/text())[1]', 'varchar(500)'),
AccountInfo.value('(AccountType/text())[1]', 'varchar(500)')
from @xml.nodes('/Employees/Employee/AccountInfo') root(AccountInfo);
insert dbo.AdditionalInfo ([EmployeeNumber], [FieldName], [FieldValue])
select
@EmpNumber,
AdditionalInfo.value('(FieldName/text())[1]', 'varchar(500)'),
AdditionalInfo.value('(FieldValue/text())[1]', 'varchar(500)')
from @xml.nodes('/Employees/Employee/AdditionalInfo') root(AdditionalInfo);
这是一个概念性示例,说明如何操作。
两个table,state作为parent,city作为child,与one-to-many关系。主键基于 IDENTITY
。
INSERT
到 parent table 生成新的 IDENTITY
值,这些值被捕获并存储在 table 变量中,稍后用于插入到a child table 保留外键约束。
SQL
-- DDL and sample data population, start
USE tempdb;
GO
DROP TABLE IF EXISTS #city;
DROP TABLE IF EXISTS #state;
-- parent table
CREATE TABLE #state (
stateID INT IDENTITY PRIMARY KEY,
stateName VARCHAR(30),
abbr CHAR(2),
capital VARCHAR(30)
);
-- child table (1-to-many)
CREATE TABLE #city (
cityID INT IDENTITY,
stateID INT NOT NULL FOREIGN KEY REFERENCES #state(stateID),
city VARCHAR(30),
[population] INT,
PRIMARY KEY (cityID, stateID, city)
);
-- mapping table to preserve IDENTITY ids
DECLARE @idmapping TABLE (GeneratedID INT PRIMARY KEY,
NaturalID VARCHAR(20) NOT NULL UNIQUE);
DECLARE @xml XML =
N'<root>
<state>
<StateName>Florida</StateName>
<Abbr>FL</Abbr>
<Capital>Tallahassee</Capital>
<cities>
<city>
<city>Miami</city>
<population>470194</population>
</city>
<city>
<city>Orlando</city>
<population>285713</population>
</city>
</cities>
</state>
<state>
<StateName>Texas</StateName>
<Abbr>TX</Abbr>
<Capital>Austin</Capital>
<cities>
<city>
<city>Houston</city>
<population>2100263</population>
</city>
<city>
<city>Dallas</city>
<population>5560892</population>
</city>
</cities>
</state>
</root>';
-- DDL and sample data population, end
;WITH rs AS
(
SELECT stateName = p.value('(StateName/text())[1]', 'VARCHAR(30)'),
abbr = p.value('(Abbr/text())[1]', 'CHAR(2)'),
capital = p.value('(Capital/text())[1]', 'VARCHAR(30)')
FROM @xml.nodes('/root/state') AS t(p)
)
MERGE #state AS o
USING rs ON 1 = 0
WHEN NOT MATCHED THEN
INSERT(stateName, abbr, capital)
VALUES(rs.stateName, rs.Abbr, rs.Capital)
OUTPUT inserted.stateID, rs.stateName
INTO @idmapping (GeneratedID, NaturalID);
;WITH Details AS
(
SELECT NaturalID = p.value('(StateName/text())[1]', 'VARCHAR(30)'),
city = c.value('(city/text())[1]', 'VARCHAR(30)'),
[population] = c.value('(population/text())[1]', 'INT')
FROM @xml.nodes('/root/state') AS A(p) -- parent
CROSS APPLY A.p.nodes('cities/city') AS B(c) -- child
)
INSERT #city (stateID, city, [Population])
SELECT m.GeneratedID, d.city, d.[Population]
FROM Details AS d
INNER JOIN @idmapping AS m ON d.NaturalID = m.NaturalID;
-- test
SELECT * FROM #state;
SELECT * FROM @idmapping;
SELECT * FROM #city;
我有一个 XML 如下所示:
<Employees>
<Employee>
<AccountInfo>
<AccountNumber>1234567</AccountNumber>
<AccountType>Test</AccountType>
</AccountInfo>
<DocumentType>Test Doc</DocumentType>
<Date>12/01/2020</Date>
<Description>Test Description</Description>
<ImageFileType>pdf</ImageFileType>
<ImageFileName>321.PDF</ImageFileName>
<AdditionalInfo>
<FieldName>docDescription</FieldName>
<FieldValue>ABC XYZ</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Creation Date</FieldName>
<FieldValue>12/01/2020</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Department Code</FieldName>
<FieldValue>63</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>ID No</FieldName>
<FieldValue>3214567</FieldValue>
</AdditionalInfo>
</Employee>
</Employees>
我想将这个 XML 数据插入到 3 tables EmployeeInfo
、AccountInfo
和 AdditionalInfo
中,架构如下:
EmployeeInfo
(
EmployeeNumber Int Identity(1,1) NOT NULL,
DocumentType varchar(500) NULL,
[Description] varchar(500) NULL,
ImageFileName varchar(500) NULL,
ImageFileType varchar(500) NULL,
[Date] varchar(500) NULL
);
AccountInfo
(
EmployeeNumber int NOT NULL,
AccountNumber varchar(500) NULL,
AccountType varchar(500) NULL
);
AdditionalInfo
(
EmployeeNumber int NOT NULL,
FieldName varchar(500) NULL,
FieldValue varchar(500) NULL
);
EmployeeNumber
列用于 linking AccountInfo
和 AdditionalInfo
table 与 EmployeeInfo
.
AccountInfo
table 将在节点以下:
<AccountInfo>
<AccountNumber>1234567</AccountNumber>
<AccountType>Test</AccountType>
</AccountInfo>
AdditionalInfo
table 将获得这些 XML 节点:
<AdditionalInfo>
<FieldName>docDescription</FieldName>
<FieldValue>ABC XYZ</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Creation Date</FieldName>
<FieldValue>12/01/2020</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>Department Code</FieldName>
<FieldValue>63</FieldValue>
</AdditionalInfo>
<AdditionalInfo>
<FieldName>ID No</FieldName>
<FieldValue>3214567</FieldValue>
</AdditionalInfo>
其余xml节点被插入EmployeeInfo
.
我尝试使用此处显示的查询。我能够获取 xml 数据并将其插入主 table EmployeeInfo
,但无法使用身份 link AdditionalInfo
和 AccountInfo
在 EmployeeInfo
table.
注意:我在xml.
中有多个员工节点DECLARE @EmpNumber int
DECLARE @x xml
SELECT @x = X FROM OPENROWSET (BULK 'C:\Test\Sample.xml', SINGLE_BLOB) AS EmpInfo(X)
DECLARE @hdoc int
EXEC sp_xml_preparedocument @hdoc OUTPUT, @x
INSERT INTO EmployeeInfo (DocumentType, [Description], ImageFileName, ImageFileType, [Date])
SELECT * FROM OPENXML (@hdoc, '/Employees/Employee', 2)
WITH ( DocumentType varchar(500), [Description] varchar(500), ImageFileName varchar(500), ImageFileType varchar(500), [Date] varchar(500))
SELECT @EmpNumber=SCOPE_IDENTITY()
INSERT INTO AccountInfo ([EmployeeNumber],[AccountNumber], [AccountType])
SELECT @EmpNumber, *
FROM OPENXML (@hdoc, '/Employees/Employee/AccountInfo', 2)
WITH (AccountNumber varchar(500), AccountType varchar(500))
INSERT INTO AdditionalInfo ([EmployeeNumber],[FieldName], [FieldValue])
SELECT @EmpNumber, *
FROM OPENXML (@hdoc, '/Employees/Employee/AdditionalInfo', 2)
WITH (
FieldName varchar(5000), FieldValue varchar(5000)
)
EXEC sp_xml_removedocument @hdoc
有人可以帮我解决这个问题吗?提前致谢。
避免使用 sp_xml_preparedocument、OPENXML 和 sp_xml_removedocument,因为它们效率低下,经常在忘记 sp_xml_removedocument 时导致资源泄漏,并鼓励 RBAR-like 在应该的地方构造成为 set-based RDBMS。
尽可能使用 nodes() and value(),例如以下...
insert dbo.EmployeeInfo (DocumentType, [Description], ImageFileName, ImageFileType, [Date])
select
Employee.value('(DocumentType/text())[1]', 'varchar(500)'),
Employee.value('(Description/text())[1]', 'varchar(500)'),
Employee.value('(ImageFileName/text())[1]', 'varchar(500)'),
Employee.value('(ImageFileType/text())[1]', 'varchar(500)'),
Employee.value('(Date/text())[1]', 'varchar(500)')
from @xml.nodes('/Employees/Employee') root(Employee);
declare @EmpNumber int = SCOPE_IDENTITY();
insert dbo.AccountInfo ([EmployeeNumber], [AccountNumber], [AccountType])
select
@EmpNumber,
AccountInfo.value('(AccountNumber/text())[1]', 'varchar(500)'),
AccountInfo.value('(AccountType/text())[1]', 'varchar(500)')
from @xml.nodes('/Employees/Employee/AccountInfo') root(AccountInfo);
insert dbo.AdditionalInfo ([EmployeeNumber], [FieldName], [FieldValue])
select
@EmpNumber,
AdditionalInfo.value('(FieldName/text())[1]', 'varchar(500)'),
AdditionalInfo.value('(FieldValue/text())[1]', 'varchar(500)')
from @xml.nodes('/Employees/Employee/AdditionalInfo') root(AdditionalInfo);
这是一个概念性示例,说明如何操作。
两个table,state作为parent,city作为child,与one-to-many关系。主键基于 IDENTITY
。
INSERT
到 parent table 生成新的 IDENTITY
值,这些值被捕获并存储在 table 变量中,稍后用于插入到a child table 保留外键约束。
SQL
-- DDL and sample data population, start
USE tempdb;
GO
DROP TABLE IF EXISTS #city;
DROP TABLE IF EXISTS #state;
-- parent table
CREATE TABLE #state (
stateID INT IDENTITY PRIMARY KEY,
stateName VARCHAR(30),
abbr CHAR(2),
capital VARCHAR(30)
);
-- child table (1-to-many)
CREATE TABLE #city (
cityID INT IDENTITY,
stateID INT NOT NULL FOREIGN KEY REFERENCES #state(stateID),
city VARCHAR(30),
[population] INT,
PRIMARY KEY (cityID, stateID, city)
);
-- mapping table to preserve IDENTITY ids
DECLARE @idmapping TABLE (GeneratedID INT PRIMARY KEY,
NaturalID VARCHAR(20) NOT NULL UNIQUE);
DECLARE @xml XML =
N'<root>
<state>
<StateName>Florida</StateName>
<Abbr>FL</Abbr>
<Capital>Tallahassee</Capital>
<cities>
<city>
<city>Miami</city>
<population>470194</population>
</city>
<city>
<city>Orlando</city>
<population>285713</population>
</city>
</cities>
</state>
<state>
<StateName>Texas</StateName>
<Abbr>TX</Abbr>
<Capital>Austin</Capital>
<cities>
<city>
<city>Houston</city>
<population>2100263</population>
</city>
<city>
<city>Dallas</city>
<population>5560892</population>
</city>
</cities>
</state>
</root>';
-- DDL and sample data population, end
;WITH rs AS
(
SELECT stateName = p.value('(StateName/text())[1]', 'VARCHAR(30)'),
abbr = p.value('(Abbr/text())[1]', 'CHAR(2)'),
capital = p.value('(Capital/text())[1]', 'VARCHAR(30)')
FROM @xml.nodes('/root/state') AS t(p)
)
MERGE #state AS o
USING rs ON 1 = 0
WHEN NOT MATCHED THEN
INSERT(stateName, abbr, capital)
VALUES(rs.stateName, rs.Abbr, rs.Capital)
OUTPUT inserted.stateID, rs.stateName
INTO @idmapping (GeneratedID, NaturalID);
;WITH Details AS
(
SELECT NaturalID = p.value('(StateName/text())[1]', 'VARCHAR(30)'),
city = c.value('(city/text())[1]', 'VARCHAR(30)'),
[population] = c.value('(population/text())[1]', 'INT')
FROM @xml.nodes('/root/state') AS A(p) -- parent
CROSS APPLY A.p.nodes('cities/city') AS B(c) -- child
)
INSERT #city (stateID, city, [Population])
SELECT m.GeneratedID, d.city, d.[Population]
FROM Details AS d
INNER JOIN @idmapping AS m ON d.NaturalID = m.NaturalID;
-- test
SELECT * FROM #state;
SELECT * FROM @idmapping;
SELECT * FROM #city;