import dynamic xml to sql server - 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;

Related

Insert XML data into multiple tables in 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);

Adventure Works 2019: Adding New Person to Person.Person Table

Right now I am using 3 stored procedures to Add a person to the person.person table. I would like to cut this down to a single stored procedure to resolve this issue.
INSERT new GUID and DateModified to table Person.BusinessEntity
SELECT the auto generate BusinessEntityID form table Person.BusinessEntity
INSERT new Person to Person.Person Table
The Stored Procedures all use parameters which I pass via a C# application and I have confirmed that the a user is in fact added to the AdventureWorks2019 Db.
Procedure: Person.CreateNewBusinessEntity
INSERT INTO [Person].[BusinessEntity]
(
[BusinessEntity].rowguid
, [BusinessEntity].ModifiedDate
)
VALUES
(
#RowGUID
, GetDate()
)
Procedure: Person.GetBusinessEntityID
SELECT
[BusinessEntityID]
FROM
[AdventureWorks2019].[Person].[BusinessEntity]
WHERE [rowguid] = #RowGuid
Procedure: Person.CreateNewPerson
INSERT INTO [Person].[Person]
(
[BusinessEntityID]
,[PersonType]
,[NameStyle]
,[Title]
,[FirstName]
,[MiddleName]
,[LastName]
,[Suffix]
,[EmailPromotion]
,[AdditionalContactInfo]
,[Demographics]
,[rowguid]
,[ModifiedDate]
)
VALUES
(
#BusinessEntityID
, #PersonType
, #NameStyle
, #Title
, #FirstName
, #MiddleName
, #LastName
, #Suffix
, #EmailPromotion
, #AdditionalContactInfo
, #Demographics
, #RowGUID
, GetDate()
)
Any help here is appreciated. Thanks!
Thanks to HABO, I am now using this solution. Now I only need two procedures.
DECLARE #Inserted table ( [BusinessEntityID] int );
INSERT INTO [Person].[BusinessEntity]
(
[BusinessEntity].rowguid
, [BusinessEntity].ModifiedDate
)
OUTPUT inserted.[BusinessEntityID] INTO #Inserted([BusinessEntityID])
VALUES
(
#RowGUID
, GetDate()
)
The below answer shows you how to get the inserted id and add it to the next insert in side the Same SP.
USE AdventureWorks2012
GO
CREATE PROC CreateNewPerson
AS
BEGIN
DECLARE #OutputTbl TABLE ([BusinessEntityID] INT, ModifiedDate DATETIME)
DECLARE #BusinessEntityID AS INT
INSERT INTO [Person].[BusinessEntity]
(
[BusinessEntity].rowguid
, [BusinessEntity].ModifiedDate
)
--Get the output value inserted to a table.
OUTPUT inserted.[BusinessEntityID], inserted.ModifiedDate INTO
#OutputTbl([BusinessEntityID],[ModifiedDate])
VALUES
(
NEWID()
, GetDate()
)
--Assigned to a variable. You can get this using subquery as well inside the insert statment.
SELECT #BusinessEntityID = [BusinessEntityID] FROM #OutputTbl
INSERT INTO [Person].[Person]
(
[BusinessEntityID]
,[PersonType]
,[NameStyle]
,[Title]
,[FirstName]
,[MiddleName]
,[LastName]
,[Suffix]
,[EmailPromotion]
,[AdditionalContactInfo]
,[Demographics]
,[rowguid]
,[ModifiedDate]
)
VALUES
(
#BusinessEntityID
, #PersonType --These columns with # sign needed to be declared or supply values
, #NameStyle
, #Title
, #FirstName
, #MiddleName
, #LastName
, #Suffix
, #EmailPromotion
, #AdditionalContactInfo
, #Demographics
, NEWID() --This will generate a new GUID for each row.
, GetDate()
)
END
GO
Second way is using Scope_identity(). Replace the
"SELECT #BusinessEntityID = [BusinessEntityID] FROM #OutputTbl'
from below lines in the Sp will do the same thing for you.
SELECT #BusinessEntityID = SCOPE_IDENTITY() --[BusinessEntityID] FROM #OutputTbl
Select #BusinessEntityID

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;

Get multiple values from XML field stored as Varchar(max)

I have this value in a varchar(max) column in SQL Server:
<VersionSeries><SeriesTypeIdList><int>3</int><int>4</int><int>2</int><int>29</int><int>31</int><int>32</int><int>39</int></SeriesTypeIdList></VersionSeries
I want to get the int values out into a table.
This is the code I have but it is only returning the first record it finds.
Declare #Version varchar(100)
Select #Version = '2016A Demo'
DECLARE #DataTable TABLE
(
Xml XML NOT NULL,
Code NVARCHAR(50) NULL
)
INSERT
INTO #DataTable(Xml)
SELECT
CONVERT(XML,CONVERT(NVARCHAR(max), Series))
FROM Version
where VersionName = #Version
Create table #SeriesCodes
(Code integer)
Insert Into #SeriesCodes
(Code)
SELECT
T.c.value('int[1]', 'nvarchar(50)') as Code
FROM #DataTable d
OUTER APPLY d.Xml.nodes('/VersionSeries/SeriesTypeIdList') T(c);
Select * from #SeriesCodes
This is a single statement to get what you want:
DECLARE #x2 XML = N'<VersionSeries><SeriesTypeIdList><int>3</int><int>4</int><int>2</int><int>29</int><int>31</int><int>32</int><int>39</int></SeriesTypeIdList></VersionSeries>';
SELECT t.c.query(N'.').value(N'(/*)[1]', N'int') AS [int_value]
FROM #X2.nodes(N'/VersionSeries/SeriesTypeIdList/*') AS [t]([c]);
Like this:
Declare #Version varchar(100)
Select #Version = '2016A Demo'
DECLARE #DataTable TABLE
(
Xml XML NOT NULL,
Code NVARCHAR(50) NULL
)
INSERT
INTO #DataTable(Xml)
values (N'<VersionSeries><SeriesTypeIdList><int>3</int><int>4</int><int>2</int><int>29</int><int>31</int><int>32</int><int>39</int></SeriesTypeIdList></VersionSeries>')
SELECT
T.c.value('.', 'nvarchar(50)') as Code
FROM #DataTable d
OUTER APPLY d.Xml.nodes('/VersionSeries/SeriesTypeIdList/int') T(c);

Resources