I trying to read a XML file in SQL Server using this guide.
http://blog.sqlauthority.com/2009/02/13/sql-server-simple-example-of-reading-xml-file-using-t-sql/
Works fine, but I have a XML file with a namespace and doesn´t works this code with namespace.
Some solution?
Thanks Remus. Now I have this code
DECLARE #MyXML XML
SET #MyXML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" Moneda="USD">
<cfdi:Impuestos totalImpuestosRetenidos="0.00" totalImpuestosTrasladados="1143.06">
</cfdi:Impuestos>
</cfdi:Comprobante> '
;with xmlnamespaces('http://www.sat.gob.mx/cfd/3' as cfdi)
SELECT
a.b.value('#Moneda','varchar(100)') Moneda,
a.b.value('Impuestos[1]/#totalImpuestosTrasladados','float') totalImpuestosTraslados
FROM #MyXML.nodes('cfdi:Comprobante') a(b)
Works, but the value of totalImpuestosTraslados is NULL.
Moneda totalImpuestosTraslados
USD NULL
Use WITH XMLNAMESPACES. Post details (what you tried, what error you got) if this information is insufficient.
DECLARE #MyXML XML
SET #MyXML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" Moneda="USD">
<cfdi:Impuestos totalImpuestosRetenidos="0.00" totalImpuestosTrasladados="1143.06">
</cfdi:Impuestos>
</cfdi:Comprobante> '
;with xmlnamespaces('http://www.sat.gob.mx/cfd/3' as cfdi)
SELECT
a.b.value('#Moneda','varchar(100)') Moneda,
a.b.value('cfdi:Impuestos[1]/#totalImpuestosTrasladados','float') totalImpuestosTraslados
FROM #MyXML.nodes('cfdi:Comprobante') a(b)
"Impuestos" also have cfdi as namespace, so you have to include it too
DECLARE #MyXML XML
SET #MyXML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" Moneda="USD">
<cfdi:Impuestos totalImpuestosRetenidos="0.00" totalImpuestosTrasladados="1143.06">
</cfdi:Impuestos>
</cfdi:Comprobante> '
;with xmlnamespaces('http://www.sat.gob.mx/cfd/3' as cfdi)
SELECT
a.b.value('#Moneda','varchar(100)') Moneda,
a.b.value('<b>/cfdi:</b>Impuestos[1]/#totalImpuestosTrasladados','float') totalImpuestosTraslados
FROM #MyXML.nodes('cfdi:Comprobante') a(b)
Related
I've been through various posts on the same subject but I can't seem to be able to get to the data elements in my XML file.
Here is a snippet of my XML :
<ed:Certificate xmlns="http://sancrt.mpi.govt.nz/ecert/2013/ed-multiple-submission-schema.xsd" xmlns:ed="http://sancrt.mpi.govt.nz/ecert/2013/ed-submission-schema.xsd"> <ed:Status Code="39">Approved</ed:Status> <ed:LastUpdatedDate>2021-03-10T14:20:55+13:00</ed:LastUpdatedDate> <ed:Identifiers>
<ed:CertificateID>NZL2021/MEABC/26913T</ed:CertificateID>
<ed:TemplateID>ED1.6</ed:TemplateID> </ed:Identifiers> <ed:Exhausted>true</ed:Exhausted> <ed:AutoApproval>false</ed:AutoApproval> <ed:DepartureDate>2021-03-10</ed:DepartureDate> <ed:Parties>
<ed:ConsignorID>MEABC</ed:ConsignorID>
<ed:ConsigneeID>FLIGHT1</ed:ConsigneeID> </ed:Parties> <ed:Transport>
<ed:Ports>
<ed:LoadingPortID>NZTRG</ed:LoadingPortID>
</ed:Ports>
<ed:FinalDestination>OAKLAND, United States</ed:FinalDestination>
<ed:TransportMode>1</ed:TransportMode>
<ed:LocalCarrier>MDH2</ed:LocalCarrier>
<ed:CarrierName> Ever Given</ed:CarrierName>
<ed:ConveyanceReference>V1234</ed:ConveyanceReference> </ed:Transport> <ed:Remarks>
<ed:Remark>
<ed:RemarkType>Unofficial Information</ed:RemarkType>
<ed:RemarkValue>Vessel ETD - 19/03/21\nTARE WEIGHT - 2880 KGS</ed:RemarkValue>
</ed:Remark> </ed:Remarks> <ed:Products>
<ed:Product>
<ed:ProductItem>1</ed:ProductItem>
<ed:Exhausted>true</ed:Exhausted>
<ed:Origin>AO</ed:Origin>
<ed:Description>BONELESS BEEF RUMP CAP</ed:Description>
<ed:CommonName>Bovine</ed:CommonName>
<ed:EligibilityCountries>
<ed:EligibilityCountryID>US</ed:EligibilityCountryID>
</ed:EligibilityCountries>
<ed:IntendedUse>consumption</ed:IntendedUse>
<ed:GrossWeight unitCode="KGM">296.4</ed:GrossWeight>
<ed:NetWeight unitCode="KGM">271.6</ed:NetWeight>
<ed:Remarks>
<ed:Remark>
<ed:RemarkType>Product Statement</ed:RemarkType>
<ed:RemarkValue>Item No. 81625\nLabel Approval 2659305 & 91060858</ed:RemarkValue>
</ed:Remark>
</ed:Remarks>
<ed:Classifications>
<ed:Classification>
<ed:ClassificationType>Temperature</ed:ClassificationType>
<ed:ClassificationValue>chilled</ed:ClassificationValue>
</ed:Classification>
<ed:Classification>
<ed:ClassificationType>New Zealand Harmonised System Code</ed:ClassificationType>
<ed:ClassificationValue>020130</ed:ClassificationValue>
</ed:Classification>
<ed:Classification>
<ed:ClassificationType>Halal Product</ed:ClassificationType>
<ed:ClassificationValue>1</ed:ClassificationValue>
</ed:Classification>
</ed:Classifications>
<ed:Containers>
<ed:Container>
<ed:ID>CGMU3099999</ed:ID>
<ed:Seals>
<ed:ID>NZMPIXXXXX</ed:ID>
</ed:Seals>
</ed:Container>
</ed:Containers>
<ed:Packaging>
<ed:Package>
<ed:Quantity>29</ed:Quantity>
<ed:Type>CT</ed:Type>
<ed:Level>1</ed:Level>
<ed:ShippingMarks>
<ed:Name>MABC\n26913</ed:Name>
</ed:ShippingMarks>
</ed:Package>
</ed:Packaging>
<ed:Processes>
<ed:Process>
<ed:ProcessTypeCode>SLT</ed:ProcessTypeCode>
<ed:StartDate>2021-03-01</ed:StartDate>
<ed:EndDate>2021-03-01</ed:EndDate>
<ed:DateOverride>false</ed:DateOverride>
<ed:Premise>
<ed:ID>MEABC</ed:ID>
</ed:Premise>
</ed:Process>
<ed:Process>
<ed:ProcessTypeCode>PRO</ed:ProcessTypeCode>
<ed:StartDate>2021-03-02</ed:StartDate>
<ed:EndDate>2021-03-02</ed:EndDate>
<ed:DateOverride>false</ed:DateOverride>
<ed:Premise>
<ed:ID>MEABC</ed:ID>
</ed:Premise>
</ed:Process>
<ed:Process>
<ed:ProcessTypeCode>CST</ed:ProcessTypeCode>
<ed:StartDate>2021-03-02</ed:StartDate>
<ed:EndDate>2021-03-10</ed:EndDate>
<ed:DateOverride>false</ed:DateOverride>
<ed:Premise>
<ed:ID>MEABC</ed:ID>
</ed:Premise>
</ed:Process>
</ed:Processes>
</ed:Product>
</ed:Products>
</ed:Certificate>
This is what I have tried so far - Figured if I can access one element, I can slowly work on the rest
if OBJECT_ID('tempdb..#XmlImportTest') is not null
drop table #XmlImportTest
CREATE TABLE #XmlImportTest(
xmlFileName VARCHAR(300) NOT NULL,
xml_data XML NOT NULL
);
DECLARE #xmlFileName VARCHAR(200) ='K:\Upload\CSNXML\WaybillXml.xml'
EXEC('INSERT INTO #XmlImportTest(xmlFileName, xml_data)
SELECT ''' + #xmlFileName + ''', xmlData
FROM(
SELECT *
FROM OPENROWSET (BULK ''' + #xmlFileName + ''', SINGLE_BLOB) AS XMLDATA
) AS FileImport (XMLDATA)
')
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #xml = (SELECT xml_data from #XmlImportTest)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #xml = (SELECT xml_data from #XmlImportTest)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
;WITH XMLNAMESPACES ('http://sancrt.mpi.govt.nz/ecert/2013/ed-multiple-submission-schema.xsd' AS ed)
SELECT
p.value(N'#ProductItem',N'nvarchar(10)') AS ProductItem
FROM
#xml.nodes('/Certificate')
AS A(p)
CROSS APPLY a.p.nodes(N'Products/Product') AS B(m);
I don't get any results returned.
I get the same result using OPENROWSET as well.
Can someone please tell me how I can access this data element.
You seem to be getting confused about XML Namespaces. The example document defines two namespace URIs:
http://sancrt.mpi.govt.nz/ecert/2013/ed-multiple-submission-schema.xsd, which has no prefix so is considered to be the "default" namespace of the document.
http://sancrt.mpi.govt.nz/ecert/2013/ed-submission-schema.xsd, which uses the ed namespace prefix that, by eyeballing it, seems to be used on every element in the document so might as well be the default namespace.
Your simplest example is trying to extract the value of the /Certificate/Products/Product/ProductItem elements which could be done as simply as:
with xmlnamespaces (
default 'http://sancrt.mpi.govt.nz/ecert/2013/ed-submission-schema.xsd'
)
select productItem.value(N'text()[1]', N'int') as ProductItem
from #xml.nodes('/Certificate/Products/Product/ProductItem') as p(productItem);
Expanding on this to select a few more values, you can see the # being used here to access the unitCode attribute of an element:
with xmlnamespaces (
default 'http://sancrt.mpi.govt.nz/ecert/2013/ed-submission-schema.xsd'
)
select
product.value(N'(ProductItem/text())[1]', N'int') as ProductItem,
product.value(N'(Exhausted/text())[1]', N'bit') as Exhausted,
product.value(N'(Origin/text())[1]', N'nvarchar(2)') as Origin,
product.value(N'(GrossWeight/text())[1]', N'decimal(19,1)') as GrossWeight,
product.value(N'(GrossWeight/#unitCode)[1]', N'nvarchar(3)') as GrossWeightUnitCode
from #xml.nodes('/Certificate/Products/Product') as p(product);
It should be clear from the above two queries that the namespace prefixes used XPath query don't have to be the same as the ones used in the XML document - it's the namespace URIs themselves that matter. The prefixes in the document are used to link the elements (and sometimes attributes) to their namespace URIs, the prefixes used in XPath can be completely different so long as they reference the correct namespace URIs. e.g. this query returns the same result as the second example above, despite their being no submission prefixes in the source XML:
with xmlnamespaces (
'http://sancrt.mpi.govt.nz/ecert/2013/ed-multiple-submission-schema.xsd' as multiple,
'http://sancrt.mpi.govt.nz/ecert/2013/ed-submission-schema.xsd' as submission
)
select
product.value(N'(submission:ProductItem/text())[1]', N'int') as ProductItem,
product.value(N'(submission:Exhausted/text())[1]', N'bit') as Exhausted,
product.value(N'(submission:Origin/text())[1]', N'nvarchar(2)') as Origin,
product.value(N'(submission:GrossWeight/text())[1]', N'decimal(19,1)') as GrossWeight,
product.value(N'(submission:GrossWeight/#unitCode)[1]', N'nvarchar(3)') as GrossWeightUnitCode,
product.value(N'(submission:Remarks/submission:Remark/submission:RemarkType/text())[1]', N'nvarchar(50)') as item_remark
from #xml.nodes('/submission:Certificate/submission:Products/submission:Product') as p(product);
I'm using SSIS\SQL Server 2014 and am using the below SQL statement to output to XML.
In summary, 1 property can have many schedules. I only want the name space to appear once before the 'Job' XML path, however, if there are multiple schedules, then the namespace is also appearing in the xml output for each schedule - how do I stop this?
My code:
DECLARE #XMLOutput XML
DECLARE #XMLOutputChar nvarchar(max)
;WITH XMLNAMESPACES('http://www.example.com' as ns)
SELECT #XMLOutput =
(
SELECT
ISNULL(T1.[PropertyID],'') as PropertyId,
ISNULL(T1.[Desc],'') as JobDescription,
(
SELECT
ScheduleId as ScheduleId,
LocationID as LocationId,
FROM [JobSchedules]
WHERE T1.JobIdentity = JobIdentity
FOR XML PATH('JobSchedules'), TYPE
)
FROM [JobFile] T1
FOR XML PATH('Job'),TYPE,ROOT('JobLoader')
)
SET #XMLOutputChar = '<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(nvarchar(max),#XMLOutput)
SELECT #XMLOutputChar AS XMLOutput
This is the desired output:
<?xml version="1.0" encoding="utf-8"?>
<JobLoader xmlns="http://www.example.com/" >
<Job>
<PropertyId>PROPERTYID</PropertyId>
<JobDescription>JOBDESCRIPTION</JobDescription>
<JobSchedules>
<JobSchedule>
<ScheduleId>SCHEDULEID</ScheduleId>
<LocationId>LOCATIONID</LocationId>
</JobSchedule>
</JobSchedules>
</Job>
</JobLoader>
What am I missing?
If anyone is interested I was able to resolve this by adding the below line to replace the value just after the encoding line:
SET #XMLOutputChar = REPLACE(#XMLOutputChar,'<JobSchedules xmlns:ns="http://www.example.com/"','<JobSchedules ')
Thanks to a fellow SO user I'm making progress with FOR XML, but I'm clearly not quite getting the sql syntax correct.
Let's say that I have the following sql
DECLARE #Uname VARCHAR(15) = 'Dom',
#Pword VARCHAR(15) = 'Monty'
SELECT
RTRIM(#Uname) AS '#uname',
RTRIM( #Pword) AS '#pword',
(SELECT COALESCE(PortOfLanding,'') AS portOfLanding
FROM Landings.LandingHeaders
WHERE Posted = 0
FOR XML PATH('Sale'))
FOR XML PATH('abc')
When run it produces the following as its output
<abc uname="Dom" pword="Monty"><Sale><portOfLanding>GBHTG</portOfLanding></Sale><Sale><portOfLanding>GBHTG</portOfLanding></Sale></abc>
What I was really hoping for though was the following
<abc uname="Dom" pword="Python">
<Sale portOfLanding= "GBHTG" />
<Sale portOfLanding= "GBHTG"/>
</abc>
and in fact I would like to add a third section to the SQL so that eventually one might end up with xml like so
<abc uname="Dom" pword="Python">
<Sale portOfLanding= "GBHTG">
<saleline detail="some value here" />
<saleline detail="some value here" />
<Sale/>
<Salesnote portOfLanding= "GBHTG"/>
</abc>
Can someone point out where I've gone wrong in the original SQL query?
Thanks
Try this:
DECLARE #Uname VARCHAR(15) = 'Dom',
#Pword VARCHAR(15) = 'Monty';
SELECT RTRIM(#Uname) AS '#uname',
RTRIM( #Pword) AS '#pword',
(SELECT COALESCE(PortOfLanding,'') AS '#portOfLanding'
FROM Landings.LandingHeaders
WHERE Posted = 0
FOR XML PATH('Sale'), TYPE)
FOR XML PATH('abc')
You can go deeper like:
SELECT RTRIM(#Uname) AS '#uname',
RTRIM( #Pword) AS '#pword',
(SELECT COALESCE(PortOfLanding,'') AS '#portOfLanding',
(SELECT COALESCE(PortOfLanding,'') AS '#detail'
FROM Landings.LandingHeaders
FOR XML PATH('SaleLine'), TYPE)
FROM Landings.LandingHeaders
WHERE Posted = 0
FOR XML PATH('Sale'), TYPE)
FOR XML PATH('abc')
This is my code.......
DECLARE #XML AS XML;
SET #XML = CAST('<Session id="ID969138672" realTimeID="4300815712">
<VarValues>
<varValue id="ID123" source="Internal" name="DisconnectedBy">VisitorClosedWindow</varValue>
<varValue id="ID1234" source="PreChat" name="email">1234#mail.ru</varValue>
</VarValues>
</Session>
' AS XML)
SELECT
xmlData.Col.value('#id','varchar(max)')
,xmlData.Col.value('#source','varchar(max)')
,xmlData.Col.value('#name','varchar(max)')
FROM #XML.nodes('//Session/VarValues/varValue') xmlData(Col);
This is the output.....
How can I include the actual values of the varValue?
I need to read the values VisistorClosedWindow and 1234#mail.ru values as well
You can get that by doing this:
xmlData.Col.value('.','varchar(max)')
So the select would be:
SELECT
xmlData.Col.value('#id','varchar(max)')
,xmlData.Col.value('#source','varchar(max)')
,xmlData.Col.value('#name','varchar(max)')
,xmlData.Col.value('.','varchar(max)')
FROM #XML.nodes('//Session/VarValues/varValue') xmlData(Col);
Just use the .value('.', 'varchar(50)) line for that:
SELECT
xmlData.Col.value('#id','varchar(25)'),
xmlData.Col.value('#source','varchar(50)'),
xmlData.Col.value('#name','varchar(50)'),
xmlData.Col.value('.','varchar(50)') -- <== this gets your the element's value
FROM #XML.nodes('//Session/VarValues/varValue') xmlData(Col);
I have the following XML generated from various tables in my SQL SERVER database
<XMLData>
...
<Type>1</Type>
...
</XMLData>
AND
<XMLData>
...
<Type>2</Type>
...
</XMLData>
AND
<XMLData>
...
<Type>3</Type>
...
</XMLData>
The final output I need is single combined as follows:
<AllMyData>
<XMLData>
...
<Type>1</Type>
...
</XMLData>
<XMLData>
...
<Type>2</Type>
...
</XMLData>
<XMLData>
...
<Type>3</Type>
...
</XMLData>
<AllMyData>
NOTE - all the independent elements that I am combining have the same tag name.
Thanks in advance for looking this up.
I have the following XML generated from various tables in my SQL
SERVER database
Depends on how you have it but if it is in a XML variable you can do like this.
declare #XML1 xml
declare #XML2 xml
declare #XML3 xml
set #XML1 = '<XMLData><Type>1</Type></XMLData>'
set #XML2 = '<XMLData><Type>2</Type></XMLData>'
set #XML3 = '<XMLData><Type>3</Type></XMLData>'
select #XML1, #XML2, #XML3
for xml path('AllMyData')
I can't comment but can answer so even though I think a comment is more appropriate, I'll expand on what rainabba answered above to add a bit more control. My .Net code needs to know the column name returned so I can't rely on auto-generated names but needed the very tip rainabba provided above otherwise.
This way, the xml can effectively be concatenated into a single row and the resulting column named. You could use this same approach to assign the results to an XML variable and return that from a PROC also.
SELECT (
SELECT XmlData as [*]
FROM
(
SELECT
xmlResult AS [*]
FROM
#XmlRes
WHERE
xmlResult IS NOT NULL
FOR XML PATH(''), TYPE
) as DATA(XmlData)
FOR XML PATH('')
) as [someColumnName]
If you use for xml type, you can combine the XML columns without casting them. For example:
select *
from (
select (
select 1 as Type
for xml path(''), type
)
union all
select (
select 2 as Type
for xml path(''), type
)
union all
select (
select 3 as Type
for xml path(''), type
)
) as Data(XmlData)
for xml path(''), root('AllMyData'), type
This prints:
<AllMyData>
<XmlData>
<Type>1</Type>
</XmlData>
<XmlData>
<Type>2</Type>
</XmlData>
<XmlData>
<Type>3</Type>
</XmlData>
</AllMyData>
As an addendum to Mikael Eriksson's answer - If you have a process where you need to continually add nodes and then want to group that under a single node, this is one way to do it:
declare #XML1 XML
declare #XML2 XML
declare #XML3 XML
declare #XMLSummary XML
set #XML1 = '<XMLData><Type>1</Type></XMLData>'
set #XMLSummary = (SELECT #XMLSummary, #XML1 FOR XML PATH(''))
set #XML2 = '<XMLData><Type>2</Type></XMLData>'
set #XMLSummary = (SELECT #XMLSummary, #XML2 FOR XML PATH(''))
set #XML3 = '<XMLData><Type>3</Type></XMLData>'
set #XMLSummary = (SELECT #XMLSummary, #XML3 FOR XML PATH(''))
SELECT #XMLSummary FOR XML PATH('AllMyData')
I needed to do the same but without knowing how many rows/variables were concerned and without extra schema added so here was my solution. Following this pattern, I can generate as many snippets as I want, combine them, pass them between PROCS or even return them from procs and at any point, wrap them up in containers all without modifying the data or being forced to add XML structure into my data. I use this approach with HTTP end points to provide XML Web services and with another trick that converts XML into JSON, to provide JSON WebServices.
-- SETUP A type (or use this design for a Table Variable) to temporarily store snippets into. The pattern can be repeated to pass/store snippets to build
-- larger elements and those can be further combined following the pattern.
CREATE TYPE [dbo].[XMLRes] AS TABLE(
[xmlResult] [xml] NULL
)
GO
-- Call the following as much as you like to build up all the elements you want included in the larger element
INSERT INTO #XMLRes ( xmlResult )
SELECT
(
SELECT
'foo' '#bar'
FOR XML
PATH('SomeTopLevelElement')
)
-- This is the key to "concatenating" many snippets into a larger element. At the end of this, add " ,ROOT('DocumentRoot') " to wrapp them up in another element even
-- The outer select is a time from user2503764 that controls the output column name
SELECT (
SELECT XmlData as [*]
FROM
(
SELECT
xmlResult AS [*]
FROM
#XmlRes
WHERE
xmlResult IS NOT NULL
FOR XML PATH(''), TYPE
) as DATA(XmlData)
FOR XML PATH('')
) as [someColumnName]
ALTER PROCEDURE usp_fillHDDT #Code int
AS
DECLARE #HD XML,#DT XML;
SET NOCOUNT ON;
select invhdcode, invInvoiceNO,invDate,invCusCode,InvAmount into #HD
from dbo.trnInvoiceHD where invhdcode=#Code
select invdtSlNo No,invdtitemcode ItemCode,invdtitemcode ItemName,
invDtRate Rate,invDtQty Qty,invDtAmount Amount ,'Kg' Unit into #DT from
dbo.trnInvoiceDt where invDtTrncode=#Code
set #HD = (select * from #HD HD FOR XML AUTO,ELEMENTS XSINIL);
set #DT = (select* from #DT DT FOR XML AUTO,ELEMENTS XSINIL);
SELECT CAST ('<OUTPUT>'+ CAST (ISNULL(#HD,'') AS VARCHAR(MAX))+ CAST ( ISNULL(#DT,'') AS VARCHAR(MAX))+ '</OUTPUT>' AS XML)
public String ReplaceSpecialChar(String inStr)
{
inStr = inStr.Replace("&", "&");
inStr = inStr.Replace("<", "<");
inStr = inStr.Replace(">", ">");
inStr = inStr.Replace("'", "'");
inStr = inStr.Replace("\"", """);
return inStr;
}