Can someone please help... I cannot seem to figure this out... I need to parse this using T-SQL. I have it saved as a file in the C:\Temp directory, named "OpenPos.XML". Any help would be very greatly appreciated. I need to be able to parse the XML and write it to a SQL table.
If I do an outer apply at all levels, it applies every PO # to every line instead of just for the lines related to that PO #.
XML File:
<?xml version="1.0" encoding="utf-8"?>
<acXML lang="en-us" xmlns="https://www.autocrib.net/acXMLSchema.xsd">
<Header>
<From>
<Company>Rolls Royce ATC (Indianapolis)</Company>
<Identity>rollsroyceatc-indianapolis</Identity>
<DatabaseName>AutoCribNet2</DatabaseName>
</From>
</Header>
<Request>
<OpenPurchaseOrderRequest ReqType="GET">
<PoNo>1716W</PoNo>
<ExternalPoNo />
<LineItems>
<Item>
<Line>1</Line>
<TagNo>1716</TagNo>
<VendorID>QMS</VendorID>
<ItemID>CARD BC6266</ItemID>
<ItemType>Expendable</ItemType>
<ItemRFID />
<Station>01</Station>
<Bin>0825-04-B-06</Bin>
<OrderQty>1</OrderQty>
<Received>1</Received>
<ReceivedBurn>0</ReceivedBurn>
<PackQty>1</PackQty>
<UnitCost>0.0000</UnitCost>
<UnitPrice>0.0000</UnitPrice>
<Lot />
<IsSpotBuy>False</IsSpotBuy>
<SpotTranCode>0</SpotTranCode>
<Inspect>False</Inspect>
<InspDate />
<InspOnHand>0</InspOnHand>
<InspBurn>0</InspBurn>
<OrderDate>01-13-2022</OrderDate>
<DueDate>01-25-2022</DueDate>
<PromiseDt>01-25-2022</PromiseDt>
<ReceiveDt />
<Department />
<Job />
<Machine />
<Reason />
<Part />
<Processed>False</Processed>
</Item>
</LineItems>
</OpenPurchaseOrderRequest>
<OpenPurchaseOrderRequest ReqType="GET">
SQL:
--Open POs
SELECT PO_X = CAST(BulkColumn AS xml)
INTO #PO
FROM OPENROWSET(BULK 'C:\AutoCrib WebServices\XML Files\ATC\OpenPOs.XML', SINGLE_CLOB) t
SELECT [Company] = p.PO_X.value('(//*:Header/*:From/*:Company)[1]', 'varchar(150)')
, [Identity] = f.value('(*:Identity)[1]', 'varchar(200)')
, [DatabaseNM] = f.value('(*:DatabaseName)[1]', 'varchar(50)')
, [PoNo] = r.value('(*:PoNo)[1]', 'varchar(10)')
, [ItemLine] = i.value('(*:Line)[1]', 'int')
, [ItemTagNo] = i.value('(*:TagNo)[1]', 'varchar(10)')
, [ItemVendorID] = i.value('(*:VendorID)[1]', 'varchar(100)')
, [ItemID] = i.value('(*:ItemID)[1]', 'varchar(30)')
, [ItemRFID] = i.value('(*:ItemRFID)[1]', 'varchar(50)')
, [ItemStation] = i.value('(*:Station)[1]', 'varchar(5)')
, [ItemBin] = i.value('(*:Bin)[1]', 'varchar(30)')
, [ItemOrderQty] = i.value('(*:OrderQty)[1]', 'int')
, [ItemReceived] = i.value('(*:Received)[1]', 'int')
, [ItemReceivedBurn] = i.value('(*:ReceivedBurn)[1]', 'int')
, [ItemPackQty] = i.value('(*:PackQty)[1]', 'int')
, [ItemUnitCost] = i.value('(*:UnitCost)[1]', 'money')
, [ItemUnitPrice] = i.value('(*:UnitPrice)[1]', 'money')
, [ItemOrderDate] = i.value('(*:OrderDate)[1]', 'datetime')
, [ItemDueDate] = i.value('(*:DueDate)[1]', 'datetime')
, [ItemPromiseDate] = i.value('(*:PromiseDt)[1]', 'datetime')
, [ItemReceiveDate] = i.value('(*:ReceiveDt)[1]', 'datetime')
, [ItemProcessed] = i.value('(*:Processed)[1]', 'varchar(10)')
FROM #PO p
OUTER APPLY p.PO_X.nodes('//*:Header/*:From') a(f)
OUTER APPLY p.PO_X.nodes('//*:Request/*:OpenPurchaseOrderRequest') c(r)
OUTER APPLY p.PO_X.nodes('//*:Request/*:OpenPurchaseOrderRequest/*:LineItems/*:Item') l(i)
DROP TABLE #PO
Start with loading the file. This uses OPENROWSET, but you might find BULK INSERT more flexible.
SELECT DepX = CAST(BulkColumn AS xml)
INTO #Departments
FROM OPENROWSET(BULK 'C:\Temp\Departments.XML', SINGLE_CLOB) t
--SINGLE_CLOB|SINGLE_NCLOB for Ascii vs Unicode
Now, we can query the xml
SELECT [From.Company] = d.DepX.value('(//*:Header/*:From/*:Company)[1]', 'varchar(99)')
/* Shorter paths if we OUTER APPLY the root we're interested in */
, [From.Company Shortcut] = f.value('(*:Company)[1]', 'varchar(99)')
FROM #Departments d
OUTER APPLY d.DepX.nodes('//*:Header/*:From') a(f)
Results
From.Company
From.Company Shortcut
Test Company (Indianapolis)
Test Company (Indianapolis)
For help with xml shredding, try Jacob Sebastian's SELECT * FROM XML
The output is helpful, but taking the time to go through and understand how it works is very educational.
This uses namespace wildcard *:
More complex xml might require WITH XMLNAMESPACE
I was finally able to figure it out. I was missing the namespace. Here's my SQL logic:
DECLARE #XML XML =
(SELECT *
FROM OPENROWSET(BULK 'C:\Temp\OpenPOs.XML', SINGLE_CLOB) t)
;WITH XMLNAMESPACES (DEFAULT 'https://www.autocrib.net/acXMLSchema.xsd')
SELECT A.evnt.value('(PoNo/text())[1]','varchar(10)') AS Event_DriverId
,B.rec.value('(Line/text())[1]','int') AS Record_RecordId
FROM #XML.nodes('/acXML/Request/OpenPurchaseOrderRequest') A(evnt)
OUTER APPLY A.evnt.nodes('LineItems/Item') B(rec);
I would like the outcome with a subset of the MakeType .
Right now I get the entire block and not elements of the MakeType.
When the XML resolves the Type, the Series and class tag an element for those should be created. This is on SQL Server Standard 2017.And I really don't know CDATA xml style and would not use it but the vendor requires the CDATA type.
---Create Temp Table
declare #RepCar table
(
[Name] varchar(10),
[Make] varchar(10),
[Model] varchar(10),
[Price] money,
[Type] varchar(10),
[Series] varchar(10),
[Class] Varchar(10)
);
insert into #RepCar
(
Name, Make, Model, Price, Type, Series, Class
)
values
('Car1', 'Make1', 'Model1', 100, 'Type1', 'IS', 'Sedan'),
('Car1', 'Make1', 'Model1', 100, 'Type1', 'LS' , 'Sport'),
('Car2', 'Make2', 'Model2', 200, 'Type2', 'M3' , 'Sport'),
('Car3', 'Make3', 'Model3', 300, 'Type3','GS350','Sedan');
--Declare Variables
DECLARE #TransactionId NVARCHAR(100)
DECLARE #TransactionDateTime DATETIME
--Setting Variable
SET #TransactionId= (SELECT CONVERT(VARCHAR, CURRENT_TRANSACTION_ID()))
SET #TransactionDateTime= GETDATE()
--Create the XML
select 1 AS Tag,
0 AS Parent,
'CollectSamplingData' as 'Message!1!TransactionType!cdata',
#TransactionId as 'Message!1!TransactionID!cdata',
#TransactionDateTime as 'Message!1!TransactionDate!cdata',
[Name] as 'Message!1!CName!cdata',
[Make] as 'Message!1!MakeCar!cdata',
[Model] as 'Message!1!MakeModel!cdata',
[Price] as 'Message!1!DataValue!cdata',
[Type] as 'Message!1!MakeType!cdata' ,
-----This is the SQL that is'nt working.
( select
1 AS Tag,
0 AS Parent,
[Series] as 'Message!2!MakeSeries!cdata',
[Class] as 'Message!2!MakeClass!cdata'
from #RepCar
FOR XML EXPLICIT
)
from #RepCar
FOR XML EXPLICIT, ROOT('Message');
The Outcome should look like this.When the code does see a the MakeType should have the Series and class below as the child element. These are the desired output XML
<Message>
<Message>
<TransactionType><![CDATA[CollectSamplingData]]></TransactionType>
<TransactionID><![CDATA[1482282230]]></TransactionID>
<TransactionDate><![CDATA[2020-02-03T11:05:17.340]]></TransactionDate>
<CName><![CDATA[Car1]]></CName>
<MakeCar><![CDATA[Make1]]></MakeCar>
<MakeModel><![CDATA[Model1]]></MakeModel>
<DataValue><![CDATA[100.0000]]></DataValue>
<MakeType><![CDATA[Type1]]>
<Series><![CDATA[IS]></Series>
<Class><![CDATA[Sedan]]></Class>
<Series><![CDATA[LS]></Series>
<Class><![CDATA[Sport]]></Class>
<Series><![CDATA[M3]></Series>
<Class><![CDATA[Sport]]></Class>
<Series><![CDATA[GS350]></Series>
<Class><![CDATA[Sedan]]></Class>>
</MakeType>
</Message>
I struggled to produce what you need by using FOR XML EXPLICIT. Eventually, I reverted to using XQuery FLWOR expression. Please remember that SQL Server XML data type cannot hold CDATA sections. You need to use the NVARCHAR(MAX) data type. Check it out here: How to use CDATA in SQL XML
SQL
-- DDL and sample data population, start
DECLARE #RepCar TABLE
(
[Name] VARCHAR(10),
[Make] VARCHAR(10),
[Model] VARCHAR(10),
[Price] MONEY,
[Type] VARCHAR(10),
[Series] VARCHAR(10),
[Class] VARCHAR(10)
);
INSERT INTO #RepCar
(
Name,
Make,
Model,
Price,
Type,
Series,
Class
)
VALUES
('Car1', 'Make1', 'Model1', 100, 'Type1', 'IS', 'Sedan'),
('Car1', 'Make1', 'Model1', 100, 'Type1', 'LS', 'Sport'),
('Car2', 'Make2', 'Model2', 200, 'Type2', 'M3', 'Sport'),
('Car3', 'Make3', 'Model3', 300, 'Type3', 'GS350', 'Sedan');
-- DDL and sample data population, end
--Declare Variables
DECLARE #TransactionId NVARCHAR(100) = CURRENT_TRANSACTION_ID();
DECLARE #TransactionDateTime DATETIME = GETDATE();
DECLARE #lt NCHAR(4) = '<'
, #gt NCHAR(4) = '>';
SELECT REPLACE(REPLACE(TRY_CAST((SELECT 'CollectSamplingData' AS [TransactionType]
, #TransactionId AS [TransactionID]
, #TransactionDateTime AS [TransactionDate]
, *
FROM #RepCar
FOR XML PATH('r'), TYPE, ROOT('root')).query('<Messages><Message>
{
for $x in /root/r[1]
return (<TransactionType>{concat("<![CDATA[", data($x/TransactionType[1]), "]]>")}</TransactionType>,
<TransactionID>{concat("<![CDATA[", data($x/TransactionID[1]), "]]>")}</TransactionID>,
<TransactionDate>{concat("<![CDATA[", data($x/TransactionDate[1]), "]]>")}</TransactionDate>,
<CName>{concat("<![CDATA[", data($x/Name[1]), "]]>")}</CName>,
<MakeCar>{concat("<![CDATA[", data($x/Make[1]), "]]>")}</MakeCar>,
<MakeModel>{concat("<![CDATA[", data($x/Model[1]), "]]>")}</MakeModel>,
<DataValue>{concat("<![CDATA[", data($x/Price[1]), "]]>")}</DataValue>,
<MakeType>{concat("<![CDATA[", data($x/Type[1]), "]]>")}
{
for $y in /root/r
return (
<Series>{concat("<![CDATA[", data($y/Series[1]), "]]>")}</Series>,
<Class>{concat("<![CDATA[", data($y/Class[1]), "]]>")}</Class>
)
}
</MakeType>)
}
</Message></Messages>') AS NVARCHAR(MAX)), #lt,'<'), #gt, '>') AS [XML with CDATA sections];
Output
<Messages>
<Message>
<TransactionType><![CDATA[CollectSamplingData]]></TransactionType>
<TransactionID><![CDATA[1149709]]></TransactionID>
<TransactionDate><![CDATA[2020-02-03T16:23:43.020]]></TransactionDate>
<CName><![CDATA[Car1]]></CName>
<MakeCar><![CDATA[Make1]]></MakeCar>
<MakeModel><![CDATA[Model1]]></MakeModel>
<DataValue><![CDATA[100.0000]]></DataValue>
<MakeType><![CDATA[Type1]]>
<Series><![CDATA[IS]]></Series>
<Class><![CDATA[Sedan]]></Class>
<Series><![CDATA[LS]]></Series>
<Class><![CDATA[Sport]]></Class>
<Series><![CDATA[M3]]></Series>
<Class><![CDATA[Sport]]></Class>
<Series><![CDATA[GS350]]></Series>
<Class><![CDATA[Sedan]]></Class>
</MakeType>
</Message>
</Messages>
Just for comparison, I would like to show how easy to implement CDATA section when the XQuery engine fully supports standards. Below is BaseX 9.3.1 implementation which is using cdata-section-elements serialization parameter: List of elements to be output as CDATA, separated by whitespaces.
Two elements <city> and <motto> are emitted as CDATA section in a simple declarative way.
XQuery
xquery version "3.1";
declare option output:omit-xml-declaration "no";
declare option output:cdata-section-elements "city motto";
declare context item := document {
<root>
<row>
<state>FL</state>
<motto>In God We Trust</motto>
<city>Miami</city>
</row>
<row>
<state>NJ</state>
<motto>Liberty and Prosperity</motto>
<city>Trenton</city>
</row>
</root>
};
<root>
{
for $r in ./root/row
return $r
}
</root>
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<row>
<state>FL</state>
<motto><![CDATA[In God We Trust]]></motto>
<city><![CDATA[Miami]]></city>
</row>
<row>
<state>NJ</state>
<motto><![CDATA[Liberty and Prosperity]]></motto>
<city><![CDATA[Trenton]]></city>
</row>
</root>
I have a SOAP response with below structure and need to get the all the values for the below tags 1. result , documentNumber , costElementCode .
This is my XML sample :
DECLARE #myXML XML = '<commitmentsResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<result xmlns="http://response.cim.its.test.edu.au/">SUCCESS</result>
<value>
<documentNumber xmlns="http://finance.cim.its.test.edu.au/">123456789</documentNumber>
<commitmentLine>
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
<commitmentLine xmlns="http://finance.cim.its.test.edu.au/">
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
</value>
<value>
<documentNumber xmlns="http://finance.cim.its.test.edu.au/">12345</documentNumber>
<commitmentLine>
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
<commitmentLine xmlns="http://finance.cim.its.test.edu.au/">
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
</value>
</commitmentsResponse>'
I have tried using the below but only get the first value and not all nodes :
DECLARE #DocumentNumber INT
;WITH XMLNAMESPACES (N'http://finance.cim.its.test.edu.au/' as DYN)
SELECT #DocumentNumber = c.value('(DYN:documentNumber)[1]', 'INT')
FROM #myXML.nodes('/commitmentsResponse/value') t(c)
DECLARE #Result VARCHAR(256)
;WITH XMLNAMESPACES (N'http://response.cim.its.test.edu.au/' as DYN)
SELECT #Result = c.value('(DYN:result)[1]', 'VARCHAR(256)')
FROM #myXML.nodes('/commitmentsResponse') t(c)
DECLARE #CostElementCode VARCHAR(256)
SELECT #CostElementCode = c.value('(costElementCode)[1]', 'VARCHAR(256)')
FROM #myXML.nodes('/commitmentsResponse/value/commitmentLine') t(c)
SELECT #Result
SELECT #DocumentNumber
SELECT #CostElementCode
You can use OUTER APPLY to shred XML data type into multiple rows. This is one example for extracting multiple costElementCodes value (implementing for the other elements should be trivial) :
SELECT x.value('.[1]', 'VARCHAR(256)') as costElementCode
FROM #myXML.nodes('/commitmentsResponse/value/commitmentLine') t(c)
OUTER APPLY t.c.nodes('costElementCode') cec(x)
with the following query Im trying to Read MessageID and DocPurpose. but only Message ID is showing fine.... Can anyone tellme what Im missing? It will be apreciated
Declare #XmlData XML =
'<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>e96d3ee4-5cca-4b3e-be60-d1284a0fb02f</MessageId>
<Company>CompanyX</Company>
<Action>http://schemas.microsoft.com/dynamics/2008/01/services/ReturnOrderInService/create</Action>
<ConversationId>320034ab-f5ee-41b0-ba35-e0669c2bf1b8</ConversationId>
<RequestMessageId>e96d3ee4-5cca-4b3e-be60-d1284a0fb02f</RequestMessageId>
</Header>
<Body>
<MessageParts>
<ReturnOrderIn xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/ReturnOrderIn">
<DocPurpose xmlns="">Original</DocPurpose>
<SenderId xmlns="">Xcorp</SenderId>
<SalesTable class="entity" xmlns="">
<_DocumentHash>63cf8580-92fe-4b17-b15c-8e619eecf71f</_DocumentHash>
<CurrencyCode>USD</CurrencyCode>
<CustAccount>147854</CustAccount>
<LanguageId>en-us</LanguageId>
<Reservation>None</Reservation>
<ReturnDeadline>2014-10-10</ReturnDeadline>
<ReturnReasonCodeId>Reason</ReturnReasonCodeId>
<SalesName>Stores</SalesName>
<SalesLine class="entity">
<DefaultDimension>
<Values xmlns="http://schemas.microsoft.com/dynamics/2008/01/sharedtypes">
<Value>
<Name>Site</Name>
<Value>VN</Value>
</Value>
</Values>
</DefaultDimension>
<ExpectedRetQty>-5</ExpectedRetQty>
<ItemId>Else</ItemId>
<Name>Something</Name>
<PriceUnit>11.00</PriceUnit>
<SalesQty>-15.00</SalesQty>
</SalesLine>
</SalesTable>
</ReturnOrderIn>
</MessageParts>
</Body>
</Envelope>'
Declare #MessageId varchar(50)
;WITH XMLNAMESPACES('http://schemas.microsoft.com/dynamics/2011/01/documents/Message' As a,
'http://schemas.microsoft.com/dynamics/2008/01/documents/ReturnOrderIn' As b,
default 'http://schemas.microsoft.com/dynamics/2011/01/documents/Message')
Select #MessageId =MessageId from
(SELECT
t.c.value('a:MessageId[1]', 'varchar(50)') MessageId
FROM #XmlData.nodes('/Envelope/Header') AS t(c)) x
Declare #DocPurpose varchar(50)
;WITH XMLNAMESPACES('http://schemas.microsoft.com/dynamics/2011/01/documents/Message' As a,
'http://schemas.microsoft.com/dynamics/2008/01/documents/ReturnOrderIn' As b,
default 'http://schemas.microsoft.com/dynamics/2011/01/documents/Message')
Select #DocPurpose = DocPurpose from
(SELECT
t.d.value('b:DocPurpose[1]', 'varchar(50)') DocPurpose
FROM #XmlData.nodes('Envelope/body/MessageParts/b:ReturnOrderIn') AS t(d))z
Print 'MessageID>>>>>>' + #MessageId
Print #DocPurpose
<DocPurpose xmlns=""> node is in empty namespace, not the default namespace. Since we can't declare a prefix to map empty namespace URI, just don't declare a default in ;WITH XMLNAMESPACES block. Try this way :
Declare #DocPurpose varchar(50)
;WITH XMLNAMESPACES
('http://schemas.microsoft.com/dynamics/2011/01/documents/Message' As a,
'http://schemas.microsoft.com/dynamics/2008/01/documents/ReturnOrderIn' As b)
Select #DocPurpose = DocPurpose from
(
SELECT t.d.value('DocPurpose[1]', 'varchar(50)') DocPurpose
FROM #XmlData.nodes('a:Envelope/a:Body/a:MessageParts/b:ReturnOrderIn') AS t(d)
)z
Small thing but matter, you need uppercase B for Body.
Please check below query.
declare #xmlRoot as xml
set #xmlRoot= '<Root>
<table1 col1="2012-03-02T16:42:55.777">
<table2Array>
<Table2 col2="abc">
</Table2>
<Table2 col2="def">
</Table2>
</table2Array>
</table1>
<table1 col1="2012-03-02T17:42:55.777">
<table2Array>
<Table2 col2="abc1">
</Table2>
<Table2 col2="def1">
</Table2>
</table2Array>
</table1>
</Root>'
declare #a as varchar(1)
set #a= '1'
SELECT
col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[1]/table2Array/Table2' ) AS T(item);
--The above query return expected output
SELECT
col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[*[local-name()=sql:variable("#a")]]/table2Array/Table2' )
AS T(item);
--The above query doesn't return expected output
what am I doing wrong here?
Since I dont have a key value in parent node to identify child node. I have to parse through index.
This worked for me:
DECLARE #a INT; -- data type is probably important!
SET #a = 1;
SELECT col1 = item.value('./#col2', 'varchar(10)')
FROM #xmlRoot.nodes('Root/table1[sql:variable("#a")]/table2Array/Table2') AS T(item);