Insert XML data into multiple tables in SQL Server - sql-server

I have a XML like below :
<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>
I want to insert this XML data into 3 tables EmployeeInfo, AccountInfo and AdditionalInfo with schema like this:
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 column is used for linking AccountInfo and AdditionalInfo table with EmployeeInfo.
AccountInfo table will get below node:
<AccountInfo>
<AccountNumber>1234567</AccountNumber>
<AccountType>Test</AccountType>
</AccountInfo>
The AdditionalInfo table will get these XML nodes:
<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>
Rest xml node are inserted into EmployeeInfo.
I tried with the query shown here. I'm able to get xml data and insert it into main table EmployeeInfo, but not able to link AdditionalInfo and AccountInfo with identity generated in EmployeeInfo table.
Note: I have multiple employee nodes in the 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
Can someone help me out in this. Thanks in Advance.

Here is a conceptual example how to do it.
Two tables, state as a parent, and city as a child, with one-to-many relationship. Primary keys are IDENTITY based.
INSERT into a parent table generates new IDENTITY values that are captured and stored in a table variable, and later used to INSERT into a child table to preserve foreign key constraint.
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;

Avoid using sp_xml_preparedocument, OPENXML and sp_xml_removedocument because they are inefficient, often cause resource leakage when sp_xml_removedocument is forgotten, and encourage RBAR-like constructs in what's supposed to be a set-based RDBMS.
Prefer to use nodes() and value() where possible, such as with the following...
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);

Related

import dynamic xml to sql server

This sample XML shown here is what I want to import into a SQL Server table. The XML tags are dynamic here.( xml element name differ in othe xml files. Also number of xml elements in root and child nodes are different for each xml file)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:heal="http://healthedge.com">
<soapenv:Header/>
<soapenv:Body>
<heal:getClaimDetailsByHccID>
<claimHccNumber>10</claimHccNumber>
<claimDetailFlags>
<includeAccounts>a</includeAccounts>
<includeAccumulators>b</includeAccumulators>
</claimDetailFlags>
<claimFilterParameters>
<includeTransientStates>true</includeTransientStates>
<mostRecentPaymentOnly>false</mostRecentPaymentOnly>
</claimFilterParameters>
</heal:getClaimDetailsByHccID>
</soapenv:Body>
</soapenv:Envelope>
I want to store xml into 3 different tables, one table for each level element(root,child, sub child) the tag names are stored as row data.
Output should look like this:
Parent table
id value
---------------------------
1 getClaimDetailsByHccID
Root table
parent_id root id name value
------------------------------------------------------
1 1 claimHccNumber 10
1 2 claimDetailFlags null
1 3 claimFilterParameters null
Child table
root id child id name value
--------------------------------------------------------
2 1 includeAccounts a
2 2 includeAccumulators b
3 3 includeTransientStates TRUE
3 4 mostRecentPaymentOnly FALSE
i tried using below code, here value is not populating correctly for rootTable code:-
DROP TABLE IF EXISTS ChildTbl;
DROP TABLE IF EXISTS RootTbl;
DROP TABLE IF EXISTS ParentTbl;
CREATE TABLE ParentTbl (ID INT IDENTITY PRIMARY KEY, [value] VARCHAR(100));
CREATE TABLE RootTbl (root_id INT IDENTITY PRIMARY KEY, parent_id INT, [name] VARCHAR(100), [value] VARCHAR(100));
CREATE TABLE ChildTbl (child_id INT IDENTITY PRIMARY KEY, parent_id INT, [name] VARCHAR(100), [value] VARCHAR(100));
DECLARE #xml XML = N'
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:heal="http://healthedge.com">
<soapenv:Header/>
<soapenv:Body>
<heal:getClaimDetailsByHccID>
<claimHccNumber>10</claimHccNumber>
<claimDetailFlags>
<includeAccounts>a</includeAccounts>
<includeAccumulators>b</includeAccumulators>
</claimDetailFlags>
<claimFilterParameters>
<includeTransientStates>true</includeTransientStates>
<mostRecentPaymentOnly>false</mostRecentPaymentOnly>
</claimFilterParameters>
</heal:getClaimDetailsByHccID>
</soapenv:Body>
</soapenv:Envelope>'
-- DDL and sample data population, end
DECLARE #ParentID INT
, #RootID INT;
-- Parent table handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(heal:*[1])','VARCHAR(100)') AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body') AS t(c)
)
INSERT INTO dbo.ParentTbl (value)
SELECT * FROM rs;
SET #ParentID = SCOPE_IDENTITY();
-- RootTbl table handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, c.value('(*[1]/text())[1]','VARCHAR(100)') AS [value]
-- ,c.value('./.','VARCHAR(100)') AS [value1]
FROM #xml.nodes('/*/*/*/*') AS t(c)
)
INSERT INTO dbo.RootTbl (parent_id, [name], [value])
SELECT #ParentID, * FROM rs;
SET #RootID = SCOPE_IDENTITY();
----child----
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, c.value('./.','VARCHAR(100)') AS [value]
FROM #xml.nodes('/*/*/*/*/*') AS t(c)
)
INSERT INTO dbo.ChildTbl (parent_id, [name], [value])
SELECT #RootID, * FROM rs;
-- test
SELECT * FROM dbo.ParentTbl;
SELECT * FROM dbo.RootTbl
SELECT * FROM dbo.ChildTbl;
It is not so clear what it means "...The XML tags are dynamic here...".
Please try the following solution.
USE tempdb;
GO
-- DDL and sample data population, start
DROP TABLE IF EXISTS dbo.ChildTbl;
DROP TABLE IF EXISTS dbo.RootTbl;
DROP TABLE IF EXISTS dbo.ParentTbl;
CREATE TABLE dbo.ParentTbl (ID INT IDENTITY PRIMARY KEY, [value] VARCHAR(100));
CREATE TABLE dbo.RootTbl (root_id INT IDENTITY PRIMARY KEY
, parent_id INT NOT NULL FOREIGN KEY REFERENCES dbo.ParentTbl(ID), [name] VARCHAR(100), [value] VARCHAR(100));
CREATE TABLE dbo.ChildTbl (child_id INT IDENTITY PRIMARY KEY
, parent_id INT NOT NULL FOREIGN KEY REFERENCES dbo.RootTbl(root_id), [name] VARCHAR(100), [value] VARCHAR(100));
/*
DECLARE #xml XML = N'
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:heal="http://healthedge.com">
<soapenv:Header/>
<soapenv:Body>
<heal:getClaimDetailsByHccID>
<claimHccNumber>10</claimHccNumber>
<claimDetailFlags>
<includeAccounts>a</includeAccounts>
<includeAccumulators>b</includeAccumulators>
</claimDetailFlags>
<claimFilterParameters>
<includeTransientStates>true</includeTransientStates>
<mostRecentPaymentOnly>false</mostRecentPaymentOnly>
</claimFilterParameters>
</heal:getClaimDetailsByHccID>
</soapenv:Body>
</soapenv:Envelope>';
*/
DECLARE #xml XML = N'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:heal="http://healthedge.com">
<soapenv:Header/>
<soapenv:Body>
<heal:getClaimDetailsByHccID>
<claimHccNumber>10</claimHccNumber>
<claimDetailFlags>
<includeAccounts>1</includeAccounts>
<includeAccumulators>2</includeAccumulators>
<includeBenefitNetwork>3</includeBenefitNetwork>
<includeBenefitPlan>4</includeBenefitPlan>
<includeExternalRepricerResult>101</includeExternalRepricerResult>
<includeFundedAccounts>5</includeFundedAccounts>
<includeIngenixExternalLineResult>102</includeIngenixExternalLineResult>
<includePractitioner>6</includePractitioner>
<includeProcedureInformation>7</includeProcedureInformation>
<includeProduct>8</includeProduct>
<includeReportingCategories>9</includeReportingCategories>
<includeReportingCategoriesDescription>10</includeReportingCategoriesDescription>
<includeRepricerName>11</includeRepricerName>
<includeSupplier>12</includeSupplier>
<includeSupplierInvoice>13</includeSupplierInvoice>
<includeSupplierLocation>14</includeSupplierLocation>
<includeWithholding>15</includeWithholding>
</claimDetailFlags>
<claimFilterParameters>
<asOfDateTime>51</asOfDateTime>
<claimState>52</claimState>
<includeAllPreviouslyPaid>53</includeAllPreviouslyPaid>
<includeReversedClaimHeader>54</includeReversedClaimHeader>
<includeTransientStates>55</includeTransientStates>
<mostRecentPaymentOnly>56</mostRecentPaymentOnly>
<paymentBatchNumber>57</paymentBatchNumber>
<paymentDateRangeEnd>58</paymentDateRangeEnd>
<paymentDateRangeStart>59</paymentDateRangeStart>
<usePlanTypeFromClaimLine>60</usePlanTypeFromClaimLine>
</claimFilterParameters>
</heal:getClaimDetailsByHccID>
</soapenv:Body>
</soapenv:Envelope>';
-- DDL and sample data population, end
DECLARE #ParentID INT
, #RootID INT;
-- Parent table handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(heal:*[1])','VARCHAR(100)') AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body') AS t(c)
)
INSERT INTO dbo.ParentTbl (value)
SELECT * FROM rs;
SET #ParentID = SCOPE_IDENTITY();
-- RootTbl table handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, c.value('(./text())[1]','VARCHAR(100)') AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body/heal:getClaimDetailsByHccID/*[1]') AS t(c)
)
INSERT INTO dbo.RootTbl (parent_id, [name], [value])
SELECT #ParentID, * FROM rs;
-- claimDetailFlags handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, NULL AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body/heal:getClaimDetailsByHccID/claimDetailFlags') AS t(c)
)
INSERT INTO dbo.RootTbl (parent_id, [name], [value])
SELECT #ParentID, * FROM rs;
SET #RootID = SCOPE_IDENTITY();
-- ChildTbl handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, c.value('(./text())[1]','VARCHAR(100)') AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body/heal:getClaimDetailsByHccID/claimDetailFlags/*') AS t(c)
)
INSERT INTO dbo.ChildTbl (parent_id, [name], [value])
SELECT #RootID, * FROM rs;
-- claimFilterParameters handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, NULL AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body/heal:getClaimDetailsByHccID/claimFilterParameters') AS t(c)
)
INSERT INTO dbo.RootTbl (parent_id, [name], [value])
SELECT #ParentID, * FROM rs;
SET #RootID = SCOPE_IDENTITY();
-- ChildTbl handling
-- =========================================================================
;WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS soapenv
, 'http://healthedge.com' AS heal), rs AS
(
SELECT c.value('local-name(.)','VARCHAR(100)') AS [name]
, c.value('(./text())[1]','VARCHAR(100)') AS [value]
FROM #xml.nodes('/soapenv:Envelope/soapenv:Body/heal:getClaimDetailsByHccID/claimFilterParameters/*') AS t(c)
)
INSERT INTO dbo.ChildTbl (parent_id, [name], [value])
SELECT #RootID, * FROM rs;
-- test
SELECT * FROM dbo.ParentTbl;
SELECT * FROM dbo.RootTbl
SELECT * FROM dbo.ChildTbl;

How to combine two FOR XML AUTO into 1 XML?

We are using SQL Server 2012.
Table myTbl has a one to many relationship to table myAllocation
Table ABC_myTbl has a one to many relationship to table ABC_myAllocation
The below query combined 2 FOR XML AUTO into 1 XML, but the problem is ID, SystemSource, Manager are included in element TradeTicket instead of on their own, and accountManager, unitPrice are included in element allocationRow instead of on their own.
Thank you
SELECT '<?xml version="1.0"?>'+
(SELECT
( SELECT trTicket.[id],trTicket.[manager],'PFM' as SystemSource
,allocationRow.accountNumber,allocationRow.unitPrice
FROM myTbl AS trTicket
LEFT JOIN myAllocation AS allocationRow ON allocationRow.trade_ticket_id=trTicket.id
WHERE trTicket.ID = 8779631
ORDER BY trTicket.id,allocationRow.AccountNumber
FOR XML AUTO, type)
,
(
SELECT trTicket.[id],trTicket.[manager],'CRD' as SystemSource
,allocationRow.accountNumber,allocationRow.unitPrice
FROM ABC_myTbl AS trTicket
LEFT JOIN ABC_myAllocation AS allocationRow ON allocationRow.trade_ticket_id=trTicket.id
WHERE trTicket.ID = 8
ORDER BY trTicket.id,allocationRow.AccountNumber
FOR XML AUTO, type)
FOR XML PATH('trTickets'), ELEMENTS) AS XMLResult
This is the current result:
<?xml version="1.0"?>
<trTickets>
<trTicket id="8779631" SystemSource="PFM" manager="MCM">
<allocationRow accountNumber="292 " unit_Price="300"/>
</trTicket>
<trTicket id="8" SystemSource="CRD" manager="DOYLE">
<allocationRow unitPrice="100" accountNumber="F11 "/>
<allocationRow unitPrice="200" accountNumber="F22 "/>
</trTicket>
</trTickets>
This is the desired result that I am looking for:
<?xml version="1.0"?>
<trTickets>
<trTicket>
<id>8</id>
<manager>DOYLE</manager>
<SystemSource>CRD</SystemSource>
<allocationRow>
<accountNumber>F11</accountNumber>
<unitPrice>100</unitPrice>
</allocationRow>
<allocationRow>
<accountNumber>F22</accountNumber>
<unitPrice>200</unitPrice>
</allocationRow>
</trTicket>
<trTicket>
<id>8779631</id>
<manager>MCM</manager>
<SystemSource>PFM</SystemSource>
<allocationRow>
<accountNumber>292</accountNumber>
<unitPrice>300</unitPrice>
</allocationRow>
</trTicket>
</trTickets>
Data sample:
Table ABC_myTbl:
ID Manager
-----------
8 DOYLE
Table ABC_myAllocation:
accountNumber unitPrice
-------------------------
F11 100
F22 200
Table myTbl:
ID Manager
---------------
8779631 MCM
Table myAllocation:
accountNumber unitPrice
--------------------------
292 300
DDL for the tables and their data:
CREATE TABLE dbo.ABC_myTbl
(
ID INT NOT NULL,
MANAGER VARCHAR(10) NOT NULL
)
CREATE TABLE dbo.myTbl
(
ID INT NOT NULL,
MANAGER VARCHAR(10) NOT NULL
)
CREATE TABLE dbo.ABC_myAllocation
(
accountNumber VARCHAR(10) NOT NULL,
unitprice NUMERIC(10, 3) NOT NULL
)
CREATE TABLE dbo.myAllocation
(
accountNumber VARCHAR(10) NOT NULL,
unitprice NUMERIC(10, 3) NOT NULL
)
INSERT INTO dbo.ABC_myTbl VALUES (8,'DOYLE')
INSERT INTO dbo.ABC_myAllocation VALUES ('F11',100)
INSERT INTO dbo.ABC_myAllocation VALUES ('F22',200)
INSERT INTO dbo.myTbl VALUES (8779631,'MCM')
INSERT INTO dbo.myAllocation VALUES ('292',300)
I didn't wait for your DDL and sample data population. So I created a conceptual sample for you. Please pay attention that the tables have implied relationships and they are used in the WHERE clauses.
SQL
-- DDL and sample data population, start
DECLARE #tbl1 TABLE (ID INT PRIMARY KEY, Manager VARCHAR(20));
INSERT INTO #tbl1 (ID, Manager) VALUES
(8, 'DOYLE'),
(9, 'XYZ');
DECLARE #tbl1Child TABLE (accountNumber CHAR(3) PRIMARY KEY, ParentID INT, unitPrice DECIMAL(10,2));
INSERT INTO #tbl1Child (accountNumber, ParentID, unitPrice) VALUES
('F11', 8, 100)
,('F22', 8, 200)
,('F70', 9, 770);
DECLARE #tbl2 TABLE (ID INT PRIMARY KEY, Manager VARCHAR(20));
INSERT INTO #tbl2 (ID, Manager) VALUES
(8779631, 'MCM')
,(8779555, 'TTT');
DECLARE #tbl2Child TABLE (accountNumber CHAR(3) PRIMARY KEY, ParentID INT, unitPrice DECIMAL(10,2));
INSERT INTO #tbl2Child (accountNumber, ParentID, unitPrice) VALUES
('292', 8779631, 300)
,('255', 8779555, 500);
-- DDL and sample data population, end
SELECT TOP(1) NULL
, (
SELECT *
, (
SELECT * FROM #tbl1Child AS c
WHERE p.ID = c.ParentID
FOR XML PATH('allocation_row'), TYPE
)
FROM #tbl1 AS p
FOR XML PATH('tradeTicket'), TYPE
)
, (
SELECT *
, (
SELECT * FROM #tbl2Child AS c
WHERE p.ID = c.ParentID
FOR XML PATH('allocation_row'), TYPE
)
FROM #tbl2 AS p
FOR XML PATH('tradeTicket'), TYPE
)
FROM #tbl1
FOR XML PATH(''), TYPE, ROOT('tradeTickets');
Output
<tradeTickets>
<tradeTicket>
<ID>8</ID>
<Manager>DOYLE</Manager>
<allocation_row>
<accountNumber>F11</accountNumber>
<ParentID>8</ParentID>
<unitPrice>100.00</unitPrice>
</allocation_row>
<allocation_row>
<accountNumber>F22</accountNumber>
<ParentID>8</ParentID>
<unitPrice>200.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>9</ID>
<Manager>XYZ</Manager>
<allocation_row>
<accountNumber>F70</accountNumber>
<ParentID>9</ParentID>
<unitPrice>770.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>8779555</ID>
<Manager>TTT</Manager>
<allocation_row>
<accountNumber>255</accountNumber>
<ParentID>8779555</ParentID>
<unitPrice>500.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>8779631</ID>
<Manager>MCM</Manager>
<allocation_row>
<accountNumber>292</accountNumber>
<ParentID>8779631</ParentID>
<unitPrice>300.00</unitPrice>
</allocation_row>
</tradeTicket>
</tradeTickets>

Stored procedure which writes xml into a DB, T-SQL

I am trying to write a stored procedure which writes xml into a DB, T-SQL.
Here is my sample xml (which will have a significant number of <RECORD>s in prod environment):
<?xml version="1.0" encoding="windows-1251"?>
<DATA FORMAT_VERSION="1.0">
<RECORD>
<NAME>МІЖНАРОДНА ГРОМАДСЬКА ОРГАНІЗАЦІЯ МІЖНАРОДНА АКАДЕМІЯ БІОЕНЕРГОТЕХНОЛОГІЙ</NAME>
<SHORT_NAME>МАБЕТ</SHORT_NAME>
<EDRPOU>00011601</EDRPOU>
<ADDRESS>01001, м.Київ, Шевченківський район, ВУЛИЦЯ ПРОРІЗНА, будинок 8, офіс 426</ADDRESS>
<STAN>зареєстровано</STAN>
</RECORD>
</DATA>
I pass the path to the xml file in the #pathToXml parameter.
Here is my stored procedure:
CREATE PROCEDURE [dbo].[LegalContractorsDataSynchronize]
(
#pathToXml varchar
)
AS
BEGIN
BEGIN TRANSACTION
DELETE FROM [dbo].[LegalContractors];
INSERT INTO [dbo].[LegalContractors]([Code], [ShortName], [Name], [LegalAddress], [Status])
SELECT CONVERT([Code], [ShortName], [Name], [LegalAddress], [Status])
FROM OPENROWSET(BULK, #pathToXml, SINGLE_CLOB) as x
COMMIT TRANSACTION
END
I am using Entity Framework to call the stored procedure. The call just happens (with no errors), but the DB is not updated. I am very sure that I mistyped something in the INSERT statement. I followed this example.
Could someone point out how could I fill the three columns in my DB using the data from the respective elements in xml? The columns are Code, ShortName, Name, LegalAddress and Status.
UPDATE
After the answer being posted I tried the suggested solution. I am getting the error:
Msg 102, Level 15, State 1, Procedure LegalContractorsDataSynchronize, Line 15 [Batch Start Line 0]
Incorrect syntax near '#pathToXml'.
Here is my code:
CREATE PROCEDURE [dbo].[LegalContractorsDataSynchronize]
(
#pathToXml varchar
)
AS
BEGIN
BEGIN TRANSACTION
DELETE FROM [dbo].[LegalContractors];
;WITH XmlFile (xmlData) AS
(
SELECT TRY_CAST(BulkColumn AS XML)
FROM OPENROWSET(BULK #pathToXml, SINGLE_BLOB) AS x
)
INSERT INTO [dbo].[LegalContractors] ([Code], [ShortName], [Name], [LegalAddress], [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(100)') AS [Code]
, c.value('(SHORT_NAME/text())[1]','NVARCHAR(512)') AS [ShortName]
, c.value('(NAME/text())[1]','NVARCHAR(2048)') AS [Name]
, c.value('(ADDRESS/text())[1]','NVARCHAR(2048)') AS [LegalAddress]
, c.value('(STAN/text())[1]','NVARCHAR(100)') AS [Status]
FROM XmlFile CROSS APPLY xmlData.nodes('/DATA/RECORD') AS t(c);
COMMIT TRANSACTION
END
Please try the following. To the best of my knowledge, OPENROWSET() doesn't accept file name parameter as a variable.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (
ID INT IDENTITY PRIMARY KEY,
Code NVARCHAR(50) NOT NULL,
ShortName NVARCHAR(100) NOT NULL,
[Name] NVARCHAR(100) NOT NULL,
LegalAddress NVARCHAR(100) NOT NULL,
[Status] NVARCHAR(50) NOT NULL
);
-- DDL and sample data population, end
-- Method #1
-- XML file is hardcoded
;WITH XmlFile (xmlData) AS
(
SELECT TRY_CAST(BulkColumn AS XML)
FROM OPENROWSET(BULK 'c:\...\Ukraine.xml', /*CODEPAGE = '65001',*/ SINGLE_BLOB) AS x
)
INSERT INTO #tbl (Code, ShortName, [Name], LegalAddress, [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(50)') AS [Code]
, c.value('(SHORT_NAME/text())[1]','NVARCHAR(100)') AS [ShortName]
, c.value('(NAME/text())[1]','NVARCHAR(100)') AS [Name]
, c.value('(ADDRESS/text())[1]','NVARCHAR(100)') AS [LegalAddress]
, c.value('(STAN/text())[1]','NVARCHAR(50)') AS [Status]
FROM XmlFile CROSS APPLY xmlData.nodes('/DATA/RECORD') AS t(c);
-- test
SELECT * FROM #tbl;
-- Method #2
-- dynamic XML file name as a parameter
DECLARE #xml XML
, #sql NVARCHAR(MAX)
, #fileName VARCHAR(256) = 'c:\...\Ukraine.xml';
SET #sql = N'SELECT #xmlOut = XmlDoc FROM OPENROWSET (BULK ' + QUOTENAME(#fileName,NCHAR(39)) + ', SINGLE_BLOB) AS Tab(XmlDoc)';
EXEC master.sys.sp_executesql #sql, N'#xmlOut XML OUTPUT', #xmlOut = #xml OUTPUT;
INSERT INTO #tbl (Code, ShortName, [Name], LegalAddress, [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(50)') AS [Code]
, c.value('(SHORT_NAME/text())[1]','NVARCHAR(100)') AS [ShortName]
, c.value('(NAME/text())[1]','NVARCHAR(100)') AS [Name]
, c.value('(ADDRESS/text())[1]','NVARCHAR(100)') AS [LegalAddress]
, c.value('(STAN/text())[1]','NVARCHAR(50)') AS [Status]
FROM #xml.nodes('/DATA/RECORD') AS t(c);
-- test
SELECT * FROM #tbl;

Open XML select values with XML name space

I am trying to select some values using open xml in sql server 2012. This works when I don't have any xml name space. But whenever the below prefix get added with root element, I am not able to select values. Any suggestion how I can select values with xmlns:
xmlns="somenamspace/2006-10-31" order-no="00000001"
USE grails
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX), #rootxmlns varchar(100)
SELECT #XML = N'<order xmlns="somenamspace/2006-10-31" order-no="00000001">
<order-date>2017-07-24T20:48:57.000Z</order-date>
<original-order-no>00000001</original-order-no>
<customer>
<customer-name>abcd abcd</customer-name>
<customer-email>jjj#gmail.com</customer-email>
</customer>
<current-order-no>00000001</current-order-no>
<payments>
<payment>
<credit-card>
<card-type>VISA</card-type>
<card-number>XXXX-XXXX-XXXX-1111</card-number>
<card-holder>abcd</card-holder>
<expiration-month>1</expiration-month>
<expiration-year>2021</expiration-year>
</credit-card>
<amount>325.48</amount>
</payment>
</payments>
</order>';
SET #rootxmlns = '<root xmlns:ns1="somenamspace/2006-10-31"/>'
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML, #rootxmlns
SELECT orderNo
FROM OPENXML(#hDoc, 'ns1:order',2)
WITH
(
orderNo [varchar](50) 'original-order-no'
)
SELECT *
FROM OPENXML(#hDoc, 'ns1:order/customer',2)
WITH
(
customerName [varchar](50) 'customer-name',
customerEmail [varchar](100) 'customer-email'
)
SELECT cardType, cardNumber, cardHolder
FROM OPENXML(#hDoc, '/order/payments/payment/credit-card',2)
WITH
(
cardType [varchar](50) 'card-type',
cardNumber [varchar](100) 'card-number',
cardHolder [varchar](100) 'card-holder'
)
EXEC sp_xml_removedocument #hDoc
GO
Great, that you've found an answer yourself, but this can be solved better.
FROM OPENXML with the corresponding SPs to open and to remove a document is outdated and should not be used any more. Rather use the methods, the native XML type provides:
The following will give you at least some templates how to access the values within your XML:
DECLARE #XML AS XML=
N'<order xmlns="somenamspace/2006-10-31" order-no="00000001">
<order-date>2017-07-24T20:48:57.000Z</order-date>
<original-order-no>00000001</original-order-no>
<customer>
<customer-name>abcd abcd</customer-name>
<customer-email>jjj#gmail.com</customer-email>
</customer>
<current-order-no>00000001</current-order-no>
<payments>
<payment>
<credit-card>
<card-type>VISA</card-type>
<card-number>XXXX-XXXX-XXXX-1111</card-number>
<card-holder>abcd</card-holder>
<expiration-month>1</expiration-month>
<expiration-year>2021</expiration-year>
</credit-card>
<amount>325.48</amount>
</payment>
</payments>
</order>';
--The query
WITH XMLNAMESPACES(DEFAULT N'somenamspace/2006-10-31')
SELECT #xml.value(N'(/order/#order-no)[1]',N'int') AS OrderNumber
,#xml.value(N'(/order/order-date/text())[1]',N'datetime') AS OrderDate
,#xml.value(N'(/order/customer/customer-name/text())[1]',N'nvarchar(max)') AS CustomerName
,p.value(N'local-name(.)',N'nvarchar(max)') AS PaymentType
,p.value(N'(card-type/text())[1]','nvarchar(max)') AS CardType
,p.value(N'(../amount/text())[1]','decimal(10,4)') AS Amount
FROM #xml.nodes(N'/order/payments/payment/*[local-name()!="amount"]') AS A(p)
The result
Nr OrderDate CustomerName PaymentType CardType Amount
1 2017-07-24 20:48:57.000 abcd abcd credit-card VISA 325.4800
Explanation
Some data is taken directly via XPath out of #xml. The statement FROM #xml.nodes() will create a derived table of <payments><payment> nodes (as the wording suggests a 1:n relationship. The <amount> node is handled explicitly, the other node within <payment> is taken as payment details.
Got it,need to add name space in property level as well:
SET #rootxmlns = '<root xmlns:ns1="http://www.demandware.com/xml/impex/order/2006-10-31"/>'
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML, #rootxmlns
SELECT orderNo
FROM OPENXML(#hDoc, 'ns1:order',2)
WITH
(
orderNo [varchar](50) 'ns1:original-order-no'
)
SELECT *
FROM OPENXML(#hDoc, 'ns1:order/ns1:customer',2)
WITH
(
customerName [varchar](50) 'ns1:customer-name',
customerEmail [varchar](100) 'ns1:customer-email'
)
SELECT cardType, cardNumber, cardHolder
FROM OPENXML(#hDoc, 'ns1:order/ns1:payments/ns1:payment/ns1:credit-card',2)
WITH
(
cardType [varchar](50) 'ns1:card-type',
cardNumber [varchar](100) 'ns1:card-number',
cardHolder [varchar](100) 'ns1:card-holder'
)
EXEC sp_xml_removedocument #hDoc
GO

insert row by row data in table via openxml stored procedure in MS SQL

I m trying to insert the data VIA XML Format. And same the XML format defined as below & i wants to insert the field row by row in SQL Table. But, none of address is inserting in SQL Table, ONLY Customer information is inserting in Dummy Table.
<XML>
<Customer>
<NAME>YOGESH</NAME>
<CONTACT>YOGESH SHARMA</CONTACT>
<Mobile>123456789</Mobile>
<Status>A</Status>
<MALE>1</MALE>
<Add>
<ADD1>
<Address>AHMEDABAD</Address>
<State>GUJARAT</State>
<City>AHMEDABAD</City>
<Pincode>380016</Pincode>
</ADD1>
<ADD2>
<Address>RAJKOT</Address>
<State>GUJARAT</State>
<City>RAJKOT</City>
<Pincode>360001</Pincode>
</ADD2>
</Add>
</Customer>
</XML>
MY SP AS BELOW :
ALTER PROCEDURE [dbo].[OPENXMLDUMMY]
#xmlCustomer NTEXT
AS
BEGIN
DECLARE #DOC INT;
EXEC sp_xml_preparedocument
#DOC OUTPUT,
#xmlCustomer;
INSERT INTO Dummy
(Name,
Contact,
Mobile,
Status,
Male,
InsertDate
)
SELECT XML.NAME,
XML.Contact,
XML.Mobile,
XML.Status,
XML.Male,
GETDATE()
FROM OPENXML(#DOC, '/XML/Customer', 2) WITH(Name VARCHAR(50),
Contact VARCHAR(75), Mobile BIGINT, Status VARCHAR(10), Male VARCHAR(10),
InsertDate DATETIME) XML;
INSERT INTO DummyExtd
(
ID,
Address,
State,
City,
Pincode
) SELECT (SELECT ID FROM DUMMY WHERE Name = Name),
XML.Address,
XML.State,
XML.City,
XML.Pincode
FROM OPENXML(#DOC, '/XML/Customer/Add',2) WITH (ID INT, Address
VARCHAR(50), State VARCHAR(50), City VARCHAR(50), Pincode INT) XML;
EXEC sp_xml_removedocument #DOC;
END;
So, i just want to insert the data as below format in SQL Tables:
ID Name Contact Mobile Status Male InsertDate
1 YOGESH YOGESH SHARMA 123456789 A 1 2017-07-26 13:28:30.957
ID Address State City Pincode
1 AHMEDABAD GUJARAT AHMEDABAD 380016
1 RAJKOT GUJARAT RAJKOT 360001
So, what is the issue in my current stored procedure & needs to correct it.
Thanking you
Yogesh
Here I made one demo for the same. Please look into this.
Firstly, I created two tables which are Customer(your table name Dummy) and Customer_Address(your table name DummyText). They are look like below snaps.
Table : Customer
Table : Customer_Address
Below is your updated store procedure.
ALTER PROCEDURE [dbo].[OPENXMLDUMMY]
#xmlCustomer NTEXT
AS
BEGIN
DECLARE #DOC INT;
Declare #CustId INT;
EXEC sp_xml_preparedocument
#DOC OUTPUT,
#xmlCustomer;
INSERT INTO Customer(Name, Contact, Mobile, Status, Male, InsertDate)
SELECT XML.[NAME], XML.Contact, XML.Mobile, XML.Status, XML.Male, GETDATE() AS InsertDate
FROM OPENXML(#DOC, '/XML/Customer', 2) WITH(NAME VARCHAR(50), CONTACT VARCHAR(75), Mobile BIGINT, Status VARCHAR(10), MALE VARCHAR(10), InsertDate DATETIME) XML;
SET #CustId = SCOPE_IDENTITY()
INSERT INTO Customer_Address(Cust_Id, Address, State, City, Pincode)
SELECT #CustId AS Cust_Id, XML.Address, XML.State, XML.City, XML.Pincode
FROM OPENXML(#DOC, '/XML/Customer/Add/ADD1',2) WITH
(
Address VARCHAR(50),
State VARCHAR(50),
City VARCHAR(50),
Pincode INT
) XML;
INSERT INTO Customer_Address(Cust_Id, Address, State, City, Pincode)
SELECT #CustId AS Cust_Id, XML.Address, XML.State, XML.City, XML.Pincode
FROM OPENXML(#DOC, '/XML/Customer/Add/ADD2',2) WITH
(
Address VARCHAR(50),
State VARCHAR(50),
City VARCHAR(50),
Pincode INT
) XML;
EXEC sp_xml_removedocument #DOC;
END
Using this procedure I executed your sample xml data and it looks like below entries in both tables.
Your issue is at
(SELECT ID FROM DUMMY WHERE Name = Name)
It returns all values from DUMMY table, and causes error.
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
Solution:
-- After the first insert
Declare #DummyId int = scope_identity() -- get new inserted id from dummy
INSERT INTO DummyExtd
(
ID,
Address,
State,
City,
Pincode
) SELECT
#DummyId,
XML.Address,
XML.State,
XML.City,
XML.Pincode
FROM OPENXML(#DOC, '/XML/Customer/Add',2) WITH
( -- ID INT, --- Remove it, ID tag doesn't exist in your xml
Address VARCHAR(50),
State VARCHAR(50),
City VARCHAR(50),
Pincode INT
) XML;

Resources