Manipulate XMLNamespaces - sql-server

I have the following output from SQL using FOR XML clause:
<q17:DestinationSection xmlns:q17="http://ITrack.Transmission/2011/02/25/Objects">
<q17:DestinationCode>1</q17:DestinationCode>
<q17:DestinationName>Strada Rampei 9, Iasi</q17:DestinationName>
<q17:DestinationAddress1>Strada Rampei 9, Iasi</q17:DestinationAddress1>
<q17:DestinationAddress2>
xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
</q17:DestinationAddress2>
</q17:DestinationSection>
The DestinationSection is the main root for this block of data. Is there any possibility to do some workaround and to have something like below in the <q17:DestinationAddress2></q17:DestinationAddress2> tag?
<q17:DestinationAddress2 xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</q17:DestinationAddress2>
I have tried something but I get an error that says that I need to declare the namespace but I really don't know where to "introduce" that definition.
My SQL statement
DECLARE #XMLFINAL VARCHAR(MAX)
SET #XMLFINAL=''
DECLARE #XMLFINAL2 VARCHAR(MAX)
SET #XMLFINAL2=''
DECLARE #NUMBER NVARCHAR(100)
DECLARE #NUMBER2 NVARCHAR(100)
DECLARE #XML VARCHAR(MAX)
DECLARE #XML2 VARCHAR(MAX)
DECLARE Rec CURSOR FAST_FORWARD FOR
SELECT GID FROM PurchaseDocumentsHeader
OPEN Rec
FETCH NEXT FROM Rec INTO #NUMBER2
WHILE ##FETCH_STATUS = 0
BEGIN
SET #XML2=''
;WITH XMLNAMESPACES ('http://ITrack.Transmission/2011/02/25/Objects' as q17)
SELECT #XML2= (
SELECT
DestCode AS 'q17:DestinationCode', DestDescr 'q17:DestinationName', DestAddr AS 'q17:DestinationAddress1', DestAddr2 AS 'q17:DestinationAddress2',
DestZIP AS 'q17:DestinationZIP'
FROM PurchaseDocumentsHeader WHERE GID=#NUMBER2
FOR XML RAW('q17:DestinationSection'),ELEMENTS
)
FETCH NEXT FROM Rec INTO #NUMBER2
SET #XMLFINAL2=#XMLFINAL2+#XML2
END
CLOSE Rec DEALLOCATE Rec
EDIT
Please find below my DDL. It's a view used to extract the data from an official table.
CREATE VIEW [dbo].[PurchaseDocumentsHeader]
AS
SELECT esd.GID, esd.ADRegistrationDate, esgo.Code AS DestCode, esgo.Description AS DestDescr, esgo.Address1 AS DestAddr, esgo.fCityCode AS DestCity,
esgp.TaxRegistrationNumber AS DestZIP, 'xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' AS DestAddr2, esgo.Description AS DestRomanized,
esgo.Address1 AS DestAddress1Romanized,
'xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' AS DestAddress2Romanized, esgp.TaxRegistrationNumber AS DestZIPRom,
esgo.fCityCode AS DestCityRomanized, esgo.fCountryCode AS DestCountry, cast('DestinationGLN xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>' AS XML) AS DestGLN,
'xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' AS DestCoord
FROM ESFIDocumentTrade esd
LEFT JOIN ESGOSites esgo on esd.fDeliverySiteGID=esgo.GID
LEFT JOIN ESFITradeAccount esc on esd.fTradeAccountGID=esc.GID
LEFT JOIN ESGOPerson esgp on esc.fPersonCodeGID=esgo.GID
LEFT JOIN ESFIDocumentType est on esd.fADDocumentTypeGID=est.GID
WHERE esd.fTransitionStepCode='APPROVED' AND est.Code='CVR'
AND YEAR(esd.ADRegistrationDate)=YEAR(GETDATE())
AND MONTH(esd.ADRegistrationDate)=MONTH(GETDATE())
AND DAY(esd.ADRegistrationDate)=DAY(GETDATE())
GO
Later edit
CREATE TABLE DocPurcharseHeader
(
ADRegistrationDate date,
Code NVARCHAR(4000),
Description NVARCHAR(4000),
Address1 NVARCHAR (4000),
Address2 NVARCHAR (4000),
City NVARCHAR (4000),
ZIPCode NVARCHAR(4000)
)
INSERT INTO DocPurcharseHeader (ADRegistrationDate, Code, Description, Address1, Address2, City, ZIPCode)
VALUES('2017-10-16', '01', 'MyPOS', 'MyPOSAddress1', 'MyPOSAddress2', 'BUCHAREST', '123456')
2nd Later edit
;WITH XMLNAMESPACES ('http://ITrack.Transmission/2011/02/25/Objects' as q17)
SELECT #XMLSalesOrders=(
SELECT DestCode AS [q17:DestinationCode]
,DestDescr AS [q17:DestinationName]
,DestAddr AS [q17:DestinationAddress1]
,DestAddr2 AS [q17:DestinationAddress2]
FROM PurchaseDocumentsHeader
FOR XML PATH('q17:DestinationSection'),ELEMENTS XSINIL,ROOT('q17:DestinationSections'))
This code above is generating the below output, without XSINL directive:
<q17:DestinationSections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q17="http://ITrack.Transmission/2011/02/25/Objects">
<q17:DestinationSection>
<q17:DestinationCode>1</q17:DestinationCode>
<q17:DestinationName>Strada Rampei 9, Iasi</q17:DestinationName>
<q17:DestinationAddress1>Strada Rampei 9, Iasi</q17:DestinationAddress1>
<q17:DestinationAddress2/>
</q17:DestinationSection>
<q17:DestinationSection>

you are trying to out-trick the very mighty XML engine.
As stated in your other question:
Never do this in a CURSOR! They are bad and evil, coming directly of the hell of procedural thinking and were invented by the devil of spaghetti code...
Try it like this:
I use your table DDL an insert some data. The second row will have a NULL in address2. Normally XML will simply omit NULL values. A not existing node is read as a NULL value. But you can force NULLs to be introduced as xsi.nil="true" with ELEMENTS XSINIL:
CREATE TABLE DocPurcharseHeader
(
ADRegistrationDate date,
Code NVARCHAR(4000),
Description NVARCHAR(4000),
Address1 NVARCHAR (4000),
Address2 NVARCHAR (4000),
City NVARCHAR (4000),
ZIPCode NVARCHAR(4000)
);
INSERT INTO DocPurcharseHeader (ADRegistrationDate, Code, Description, Address1, Address2, City, ZIPCode)
VALUES('2017-10-16', '01', 'MyPOS', 'MyPOSAddress1', 'MyPOSAddress2', 'BUCHAREST', '123456')
,('2017-10-16', '01', 'MyPOS', 'MyPOSAddress1', NULL, 'BUCHAREST', '123456');
WITH XMLNAMESPACES('http://ITrack.Transmission/2011/02/25/Objects' AS q17)
SELECT Code AS [q17:DestinationCode]
,Description AS [q17:DestinationName]
,Address1 AS [q17:DestinationAddress1]
,Address2 AS [q17:DestinationAddress2]
FROM DocPurcharseHeader
FOR XML PATH('q17:DestinationSection'),ELEMENTS XSINIL,ROOT('q17:DestinationSections');
The result
<q17:DestinationSections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q17="http://ITrack.Transmission/2011/02/25/Objects">
<q17:DestinationSection>
<q17:DestinationCode>01</q17:DestinationCode>
<q17:DestinationName>MyPOS</q17:DestinationName>
<q17:DestinationAddress1>MyPOSAddress1</q17:DestinationAddress1>
<q17:DestinationAddress2>MyPOSAddress2</q17:DestinationAddress2>
</q17:DestinationSection>
<q17:DestinationSection>
<q17:DestinationCode>01</q17:DestinationCode>
<q17:DestinationName>MyPOS</q17:DestinationName>
<q17:DestinationAddress1>MyPOSAddress1</q17:DestinationAddress1>
<q17:DestinationAddress2 xsi:nil="true" />
</q17:DestinationSection>
</q17:DestinationSections>
UPDATE: About NULL values:
Try this
DECLARE #DummyTable TABLE(SomeDescription VARCHAR(500), SomeValue VARCHAR(100));
INSERT INTO #DummyTable VALUES('A real NULL value',NULL)
,('An empty string','')
,('A blank string',' ')
,('Some Text','blah blah');
WITH XMLNAMESPACES('SomeURL' AS q17)
SELECT SomeDescription AS [q17:Description]
,SomeValue AS [q17:Value]
FROM #DummyTable
FOR XML PATH('q17:row'),ELEMENTS XSINIL,ROOT('root');
To get this
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q17="SomeURL">
<q17:row>
<q17:Description>A real NULL value</q17:Description>
<q17:Value xsi:nil="true" />
</q17:row>
<q17:row>
<q17:Description>An empty string</q17:Description>
<q17:Value></q17:Value>
</q17:row>
<q17:row>
<q17:Description>A blank string</q17:Description>
<q17:Value> </q17:Value>
</q17:row>
<q17:row>
<q17:Description>Some Text</q17:Description>
<q17:Value>blah blah</q17:Value>
</q17:row>
</root>
You can see, that the real NULL is encoded as xsi:nil="true", while the empty string is shown as <q17:Value></q17:Value> (which is exactly the same as <q17:Value />).
Check this answer for some examples about NULL and empty. Check this answer to understand more about text()

Related

XML output in SQL help needed

Hi I have the following code in SQL that outputs xml between 2 tables. The output is 90% correct but if possible add to the output and then also remove some of the output text.
I am not sure if SQL has the ability to code a type of a element in the output. Please see the code below with the output. Currently if possible I would like to make 2 changes to my current output. The changes are list at the end of the port
DECLARE #ID_Rechnung int = 1978,
#XMLData xml;
WITH XMLNAMESPACES ('?xml version="1.0" encoding="UTF-8"?' as ext)
SELECT
#XMLData = xmldat.xmldataCol
FROM
(
SELECT (
SELECT
-- HIER XML Daten generieren
[InvoiceHeader].[InvoiceDate] AS 'invoice-date',
([InvoiceHeader].[InvoiceNumber]) AS 'invoice-number',
cast(replace([InvoiceHeader].[GrossValue],' ','') as decimal(18,2)) AS 'gross-total',
cast(replace([InvoiceHeader].[NetValue],' ','') as decimal(18,2)) AS 'amount-due',
[InvoiceHeader].[VatRate] AS 'tax-rate',
cast(replace([InvoiceHeader].[VatValue],' ','') as decimal(18,2)) AS 'tax-amount',
[ImagePath] AS 'image-scan-url',
[InvoiceType] AS 'document-type',
[LegalEntityVATNo] AS 'account-type/id',
[LegalEntityName] AS 'account-type/name',
[SupplierCode] as 'supplier/number',
[Currency] as 'currency/code',
(
SELECT rtrim([InvoiceLines].[LineNumber]) AS [order-line-num]
, [PONumber] as [po-number],
CAST([InvoiceLines].[UnitPrice] AS decimal(18,2)) AS Price ,
[Quantity] as quantity,
[TaxAmount] as [tax-amount],
[LineTotal] as [total],
[Decsription] as description
FROM [InvoiceLines] WHERE [InvoiceLines].[DOCID] = #id_Rechnung
FOR XML PATH('Invoice-line'), ROOT('invoice-lines'), TYPE
)
FROM [InvoiceHeader]
WHERE [InvoiceHeader].[DOCID] = #ID_Rechnung
FOR XML PATH(''), TYPE, ROOT('invoice-header')
) AS xmldataCol
) AS xmldat;
SELECT #XMLData
.query('<invoice-header>
{
for $x in /invoice-header/*[local-name()!="root"]
return $x,
for $x in /invoice-header/root/r
return <invoice-lines>/<invoice-line>{$x/*}</invoice-line></invoice-lines>
}
</invoice-header>');
Output:
<invoice-header>
<invoice-date>20180509</invoice-date>
<invoice-number>1075440</invoice-number>
<gross-total>1376.67</gross-total>
<amount-due>1197.10</amount-due>
<tax-rate>15.00%</tax-rate>
<tax-amount>179.57</tax-amount>
<image-scan-url>\\INTEL-SQL01\Attachment\2018-06-20\7e0dd165-81d6-445a-95d1-8aac686d44ed\f9a1179c-2a54-480e-b97a-ce6ac7327ae0.000</image-scan-url>
<account-type>
<id>4010112052</id>
<name>CONSOLIDATEDPOWERPROJECTS</name>
</account-type>
<supplier>
<number>12345</number>
</supplier>
<currency>
<code>ZAR</code>
</currency>
<invoice-lines xmlns:ext="?xml version="1.0" encoding="UTF-8"?">
<Invoice-line>
<order-line-num>4</order-line-num>
<po-number>120934861</po-number>
<Price>50.00</Price>
<quantity>1.000000</quantity>
<tax-amount>7.500000</tax-amount>
<total>50.00</total>
<description>Test1</description>
</Invoice-line>
<Invoice-line>
<order-line-num>2</order-line-num>
<po-number>120934861</po-number>
<Price>10.00</Price>
<quantity>2.000000</quantity>
<tax-amount>4.500000</tax-amount>
<total>20.00</total>
<description>Test2</description>
</Invoice-line>
</invoice-lines>
</invoice-header>
1.How do I get rid of the following xmlns:ext="?xml version="1.0" encoding="UTF-8&in the line: "<invoice-lines xmlns:ext="?xml version="1.0" encoding="UTF-8"?">"
How would I code "<tax-amount>7.500000</tax-amount> to get the output": "<tax-amount type="decimal">7.500000</tax-amount>"
Without a minimal reproducible example it is not possible to give you a full working answer.
(1) As #JeroenMostert already pointed out, the
'?xml version="1.0" encoding="UTF-8"?'
is an XML prolog declaration. Just delete the following line:
WITH XMLNAMESPACES ('?xml version="1.0" encoding="UTF-8"?' as ext)
(2) Here is a conceptual example how to add an attribute to an XML element. What is important here is a sequential order of adding, i.e. attribute shall be first, element itself is 2nd.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, [tax-amount] VARCHAR(20));
INSERT INTO #tbl ([tax-amount]) VALUES
(N'7.500000'),
(N'18.000000');
SELECT
'decimal' AS [tax-amount/#type]
, [tax-amount]
FROM #tbl
FOR XML PATH('r'), TYPE, ROOT('root');
Output
<root>
<r>
<tax-amount type="decimal">7.500000</tax-amount>
</r>
<r>
<tax-amount type="decimal">18.000000</tax-amount>
</r>
</root>

T-SQL cannot query XML with multiple nested namespaces and no unique ID

I need to create a query from an xml file that hcontains 4 namespaces but none of them have a unique id. eg. xmlns:fb
I have listed them below:
xmlns="http://xxx/pie/svc/frs/FindRegistration/2.0.0"
xmlns="http://xxx/pie/xsd/frs/FindRegistrationMessages/2.0.0"
xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0"
xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0"
If I strip all the namespaces (through Visual Studio) from the document I can retrieve the data fine, but I need to keep the namespaces intact.
<FindRegistrationsResponse xmlns="http://xxx/pie/svc/frs/FindRegistration/2.0.0">
<AuditDetails xmlns="http://xxx/pie/xsd/frs/FindRegistrationMessages/2.0.0">
<QueryIdentifier xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0">8901823</QueryIdentifier>
<PractitionerCount xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0">134140</PractitionerCount>
<ServiceMessagesCount xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0">2</ServiceMessagesCount>
<TotalRecordsSearched xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0">134142</TotalRecordsSearched>
<TotalNumberOfRegistration xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0">257034</TotalNumberOfRegistration>
<SnapshotDate xmlns="http://xxx/pie/xsd/common/CommonCoreElements/2.0.0">2018-06-29</SnapshotDate>
</AuditDetails>
<ProfessionNumberReplay ProfessionNumber="MED0000xxxxx" xmlns="http://xxx/pie/xsd/frs/FindRegistrationMessages/2.0.0">
<Practitioner>
<PractitionerIdentifier xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0">xxxxxxxx</PractitionerIdentifier>
<PractitionerName NameEditDate="2010-07-03T14:14:35.377" xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0">
<NameTitle>Dr</NameTitle>
<FamilyName>Smith</FamilyName>
<GivenName>John</GivenName>
<MiddleName />
</PractitionerName>
<Demographics xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0">
<Gender>GenderName</Gender>
</Demographics>
<Qualification QualificationEditDate="2016-08-10T19:35:54.067" xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0">
<QualificationTitle>Bachelor of Medicine / Bachelor of Surgery </QualificationTitle>
<AwardingInstitution>University of xxx</AwardingInstitution>
<CountryQualificationObtained>Country Name</CountryQualificationObtained>
<YearOfQualification>1999</YearOfQualification>
</Qualification>
<Address AddressEditDate="2010-07-03T14:15:48.607" xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0">
<AustralianAddress>
<AustralianLocality>SUBURB NAME</AustralianLocality>
<AustralianPostcode>2000</AustralianPostcode>
<AustralianState>NSW</AustralianState>
<Country>Australia</Country>
</AustralianAddress>
</Address>
<Profession xmlns="http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0">
<ProfessionNumber>MED0000xxxxx</ProfessionNumber>
<Profession>Medical Practitioner</Profession>
<ProfessionStartDate>1999-05-10T00:00:00</ProfessionStartDate>
<Registration>
<RecordNumber>001</RecordNumber>
<RegistrationType>Limited (Public Interest - Occasional Practice)</RegistrationType>
<RegistrationStatus>Unregistered</RegistrationStatus>
<RegistrationSubStatus>Withdrawn</RegistrationSubStatus>
<RegistrationToDate>2013-09-30T00:00:00</RegistrationToDate>
<InitialRegistrationDate>1965-05-10T00:00:00</InitialRegistrationDate>
</Registration>
<Condition ConditionEditDate="2012-10-22T14:00:10.5">
<ConditionType>Registration</ConditionType>
<ConditionDetail>text here</ConditionDetail>
</Condition>
</Profession>
</Practitioner>
</ProfessionNumberReplay>
</FindRegistrationsResponse>
This is the T-SQL code that I've tried:
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM AHPRA_XML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
;WITH XMLNAMESPACES('http://xxx/pie/svc/frs/FindRegistration/2.0.0',
'http://xxx/pie/xsd/frs/FindRegistrationMessages/2.0.0',
'http://xxx/pie/xsd/common/CommonCoreElements/2.0.0',
'http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0')
SELECT DISTINCT ProfessionNumber, NameTitle, GivenName, MiddleName, FamilyName, Gender
FROM OPENXML(#hDoc, 'FindRegistrationsResponse/ProfessionNumberReplay/Practitioner')
WITH
(
ProfessionNumber [varchar](50) '../#ProfessionNumber',
NameTitle [varchar](20) 'PractitionerName/NameTitle',
GivenName [varchar](100) 'PractitionerName/GivenName',
MiddleName [varchar](100) 'PractitionerName/MiddleName',
FamilyName [varchar](100) 'PractitionerName/FamilyName',
Gender [varchar](10) 'Demographics/Gender'
)
EXEC sp_xml_removedocument #hDoc
GO
You've defined the namespaces - but you never use them in your XPath! Also, I'd strongly recommend to use the built-in XQuery capabilities over the legacy OPENXML approach.
Try this select statement:
;WITH XMLNAMESPACES('http://xxx/pie/svc/frs/FindRegistration/2.0.0' AS root,
'http://xxx/pie/xsd/frs/FindRegistrationMessages/2.0.0' AS msg,
'http://xxx/pie/xsd/frs/PractitionerRegistrationElements/2.0.0' AS el)
SELECT
ProfessionNumber = XC.value('(../#ProfessionNumber)', 'varchar(50)'),
NameTitle = XC.value('(el:PractitionerName/el:NameTitle)[1]', 'varchar(50)'),
GivenName = XC.value('(el:PractitionerName/el:GivenName)[1]', 'varchar(50)'),
MiddleName = XC.value('(el:PractitionerName/el:MiddleName)[1]', 'varchar(50)'),
FamilyName = XC.value('(el:PractitionerName/el:FamilyName)[1]', 'varchar(50)'),
Gender = XC.value('(el:Demographics/el:Gender)[1]', 'varchar(50)')
FROM
#XML.nodes('/root:FindRegistrationsResponse/msg:ProfessionNumberReplay/msg:Practitioner') AS XT(XC)
This should grab the desired data from the XML string.
Update:
when you the XML you provided in the question, and this code here, you do get this output:
So either you're not really working with the sample XML you've provided, or you are using different code......

SQL Server insert from a XML file

I'm trying to insert info from a XML file to a temporal table in SQL Server, but I can't get it.
First I declare a table variable, then I make an insert into this table, and the values come from an XML file, at the end I select data from the table variable that should have the info inserted before, but the select just returns an empty result without errors.
Any ideas?
This is the XML
<?xml version="1.0" encoding="UTF-8"?>
<cfdi:Comprobante Moneda="MXN" NumCtaPago="3746" LugarExpedicion="something" metodoDePago="03" tipoDeComprobante="ingreso" total="434.30" descuento="0.00" subTotal="402.14"
noCertificado="00001000000403736552" formaDePago="pago en una sola exhibición" sello="something" fecha="something" folio="something" serie="something" version="3.2" xsi:schemaLocation="http://something http://something" xmlns:xsi="http://something" xmlns:cfdi="http://something">
<cfdi:Addenda xsi:schemaLocation="https://something" xmlns:xsi="http://something" xmlns="https://something">
<ADDENDABENAVIDES>
<HEADERFACTURA INTNOTAENTRADA="something" STRREMISIONID="something" STRCLAVEFACTREM= "something" FLTIEPSFACTURA="something" FLTIVADESCUENTO="something" FLTDESCUENTOFACTURA="something" FLTBRUTOFACTURA="something" FLTIVAFACTURA="something" FLTNETOFACTURA="something" STRALMACENID="something" STRCENTROLOGISTICOID="something" DTMFECHAFACTURA="something" INTNOREGISTRO="something" STRFOLIO="something" STRSERIE="something" INTBODEGAID="something" INTMAYORISTAID="something" STRNUMEROPROVEEDOR="something"/>
<DETALLEFACTURA>
<DETALLEPRODUCTO />
</DETALLEFACTURA>
</ADDENDABENAVIDES>
</cfdi:Addenda>
</cfdi:Comprobante>
And this is from SQL
DECLARE #HEADERFACTURA TABLE
(
Id int IDENTITY(1,1),
[INTNOTAENTRADA] int,
[STRREMISIONID] NVARCHAR(max),
[STRCLAVEFACTREM] NVARCHAR(max),
[FLTIEPSFACTURA] decimal(10,2),
[FLTIVADESCUENTO] decimal(10,2),
[FLTDESCUENTOFACTURA] decimal(10,2),
[FLTBRUTOFACTURA] decimal(10,2),
[FLTIVAFACTURA] decimal(10,2),
[FLTNETOFACTURA] decimal(10,2),
[STRALMACENID] int,
[STRCENTROLOGISTICOID] NVARCHAR(max),
[DTMFECHAFACTURA] NVARCHAR(max),
[INTNOREGISTRO] int,
[STRFOLIO] int,
[STRSERIE] NVARCHAR(max),
[INTBODEGAID] int,
[INTMAYORISTAID] int,
[STRNUMEROPROVEEDOR] NVARCHAR(max)
)
;with xmlnamespaces('http://something' as cfdi)
INSERT INTO #HEADERFACTURA ([INTNOTAENTRADA], [STRREMISIONID],
[STRCLAVEFACTREM], [FLTIEPSFACTURA],
[FLTIVADESCUENTO], [FLTDESCUENTOFACTURA],
[FLTBRUTOFACTURA], [FLTIVAFACTURA],
[FLTNETOFACTURA], [STRALMACENID],
[STRCENTROLOGISTICOID], [DTMFECHAFACTURA],
[INTNOREGISTRO], [STRFOLIO],
[STRSERIE], [INTBODEGAID],
[INTMAYORISTAID], [STRNUMEROPROVEEDOR])
SELECT
X.Solicitud.query('INTNOTAENTRADA').value('.', 'int'),
X.Solicitud.query('STRREMISIONID').value('.', 'nvarchar(50)'),
X.Solicitud.query('STRCLAVEFACTREM').value('.', 'nvarchar(50)'),
X.Solicitud.query('FLTIEPSFACTURA').value('.', 'decimal(10,2)'),
X.Solicitud.query('FLTIVADESCUENTO').value('.', 'decimal(10,2)'),
X.Solicitud.query('FLTDESCUENTOFACTURA').value('.', 'decimal(10,2)'),
X.Solicitud.query('FLTBRUTOFACTURA').value('.', 'decimal(10,2)'),
X.Solicitud.query('FLTIVAFACTURA').value('.', 'decimal(10,2)'),
X.Solicitud.query('FLTNETOFACTURA').value('.', 'decimal(10,2)'),
X.Solicitud.query('STRALMACENID').value('.', 'int'),
X.Solicitud.query('STRCENTROLOGISTICOID').value('.', 'nvarchar(50)'),
X.Solicitud.query('DTMFECHAFACTURA').value('.', 'nvarchar(50)'),
X.Solicitud.query('INTNOREGISTRO').value('.', 'int'),
X.Solicitud.query('STRFOLIO').value('.', 'int'),
X.Solicitud.query('STRSERIE').value('.', 'nvarchar(50)'),
X.Solicitud.query('INTBODEGAID').value('.', 'int'),
X.Solicitud.query('INTMAYORISTAID').value('.', 'int'),
X.Solicitud.query('STRNUMEROPROVEEDOR').value('.', 'nvarchar(50)')
FROM
(SELECT
CAST (X AS XML)
FROM
OPENROWSET (BULK 'C:\aa.xml', SINGLE_BLOB) AS T(X)
) AS T(X)
CROSS APPLY
x.nodes('/cfdi:Comprobante/cfdi:Addenda/ADDENDABENAVIDES/HEADERFACTURA') AS X(Solicitud);
SELECT *
FROM #HEADERFACTURA
Thanks in advance.
You have a specific namespace and default namespace in your input xml. Fix the following line and you will get results:
CROSS APPLY
x.nodes('//cfdi:Comprobante/cfdi:Addenda/*:ADDENDABENAVIDES/*:HEADERFACTURA') AS X(Solicitud);
Take note that your query will still fail from your example because all of the attributes you are querying are strings and your query is casting them into types.
Also note, you can simplify each of your attribute statements as per this example:
X.Solicitud.query('STRNUMEROPROVEEDOR').value('.', 'nvarchar(50)') becomes
X.Solicitud.value('#STRNUMEROPROVEEDOR', 'nvarchar(50)')
Lastly, notice your xml is <?xml version="1.0" encoding="UTF-8"?>; I believe it should be <?xml version="1.0" encoding="UTF-16"?> since you are using accented characters. Your XML file may fail to parse.
In your attempt to provide an XML with cleaned data you went much to far...
Values, which should be numeric (e.g. int) show up as "Something", actually everything shows up as "something"...
Another problem is, that your namespaces are all set to the same URL.
Look at this (highly condensed) sample of your XML (the default namespace is in the second level!):
<?xml version="1.0" encoding="UTF-8"?>
<cfdi:Comprobante xmlns:cfdi="http://something" Moneda="MXN">
<cfdi:Addenda xmlns="https://Default">
<ADDENDABENAVIDES>
<HEADERFACTURA INTNOTAENTRADA="123" STRREMISIONID="something" />
<DETALLEFACTURA>
<DETALLEPRODUCTO />
</DETALLEFACTURA>
</ADDENDABENAVIDES>
</cfdi:Addenda>
</cfdi:Comprobante>
One more problem is, that your XML-file starts with a declaration with encoding="utf-8", but your content includes special characters. This declaration will be ommitted by SQL Server in any case, but you cannot read UTF-8 via NVARCHAR into XML. Therefore I read this into NVARCHAR(MAX), call REPLACE to introduce utf-16 and cast this to XML.
The next point is, that you want to read attributes, but you try to find them as elements.
You would query this like here:
;WITH XMLNAMESPACES(DEFAULT 'https://Default'
,'http://something' AS cfdi)
SELECT
--An attribute from <cfdi:Comprobante>
T.X.value('(/cfdi:Comprobante/#Moneda)[1]','nvarchar(max)') AS Moneda,
--Many attributes in <HEADERFACTURA>
X.Solicitud.value('#INTNOTAENTRADA', 'int') AS INTNOTAENTRADA,
X.Solicitud.value('#STRREMISIONID', 'nvarchar(50)') AS STRREMISIONID
FROM
(SELECT
CAST(REPLACE(CAST (X AS NVARCHAR(MAX)),'utf-8','utf-16') AS XML)
FROM
OPENROWSET (BULK 'C:\aa.xml', SINGLE_CLOB) AS T(X)
) AS T(X)
CROSS APPLY
x.nodes('/cfdi:Comprobante/cfdi:Addenda/ADDENDABENAVIDES/HEADERFACTURA') AS X(Solicitud);

Formatting sql correctly tp produce expected xml output

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')

Query XML creating field names whithout knowing node names

If I have a SQL SERVER 2012 table containing an XML field type. The records it could contain are as follows.
I have simplified my problem to the following.
Record 1:
ID_FIELD='nn1'
XML_FIELD=
<KNOWN_NAME_1>
<UNKNOWN_NAME1>Some value</UNKNOWN_NAME1>
<UNKNOWN_NAME2>Some value</UNKNOWN_NAME2>
... Maybe more ...
</KNOWN_NAME_1>
Record 2:
ID_FIELD='nn2'
XML_FIELD=
<KNOWN_NAME_2>
<UNKNOWN_NAME1>Some value</UNKNOWN_NAME1>
<UNKNOWN_NAME2>Some value</UNKNOWN_NAME2>
... Maybe more unknown fields ...
</KNOWN_NAME_2>
I want to output non xml:
UNKNOWN_NAME1 | UNKNOWN_NAME2 | ETC
-----------------------------------
Some Value Some value
For a known root value (i.e. KNOWN_NAME_1)
I.e. If I new the node values (which I don't) I could
SELECT
XMLData.Node.value('UNKNOWN_NAME1[1]', 'varchar(100)') ,
XMLData.Node.value('UNKNOWN_NAME2[1], 'varchar(100)')
FROM FooTable
CROSS APPLY MyXmlField.nodes('//KNOWN_NAME_1') XMLData(Node)
-- WHERE SOME ID value = 'NN1' (all XML records have a separate id)
All is good however I want to do this for all the nodes (unknown quantity) without knowing the node names. The root will only contain nodes it wont get any deeper.
Is this possible in SQL?
I have looked at this but I doubt I can get enough rights to implement it.
http://architectshack.com/ClrXmlShredder.ashx
If you don't know the column names in the output you have to use dynamic SQL:
-- Source table
declare #FooTable table
(
ID_FIELD char(3),
XML_FIELD xml
)
-- Sample data
insert into #FooTable values
('nn1', '<KNOWN_NAME_1>
<UNKNOWN_NAME1>Some value1</UNKNOWN_NAME1>
<UNKNOWN_NAME2>Some value2</UNKNOWN_NAME2>
</KNOWN_NAME_1>')
-- ID to look for
declare #ID char(3) = 'nn1'
-- Element name to look for
declare #KnownName varchar(100) = 'KNOWN_NAME_1'
-- Variable to hold the XML to process
declare #XML xml
-- Get the XML
select #XML = XML_FIELD
from #FooTable
where ID_FIELD = #ID
-- Variable for dynamic SQL
declare #SQL nvarchar(max)
-- Build the query
select #SQL = 'select '+stuff(
(
select ',T.N.value('''+T.N.value('local-name(.)', 'sysname')+'[1]'', ''varchar(max)'') as '+T.N.value('local-name(.)', 'sysname')
from #XML.nodes('/*[local-name(.)=sql:variable("#KnownName")]/*') as T(N)
for xml path(''), type
).value('.', 'nvarchar(max)'), 1, 1, '')+
' from #XML.nodes(''/*[local-name(.)=sql:variable("#KnownName")]'') as T(N)'
-- Execute the query
exec sp_executesql #SQL,
N'#XML xml, #KnownName varchar(100)',
#XML = #XML,
#KnownName = #KnownName
Result:
UNKNOWN_NAME1 UNKNOWN_NAME2
--------------- ---------------
Some value1 Some value2
The dynamically generated query looks like this:
select T.N.value('UNKNOWN_NAME1[1]', 'varchar(max)') as UNKNOWN_NAME1,
T.N.value('UNKNOWN_NAME2[1]', 'varchar(max)') as UNKNOWN_NAME2
from #XML.nodes('/*[local-name(.)=sql:variable("#KnownName")]') as T(N)
SE-Data

Resources