I would like to know in SQL Server how do I take the following XML and represent the following table.
<Family>
<Main Surname="Smith" />
<Person Name="Fred" />
<Person Name="Jane" />
</Family>
Row Name Surname
1 Fred Smith
2 Jane Smith
Please note I cannot change the XML schema.
Here is my current SQL query.
declare #Input XML
set #Input = '<Family> <Main Surname="Smith" /> <Person Name="Fred" /> <Person Name="Jane" /> </Family>'
SELECT
Test.value('(#Name)[1]', 'varchar(20)') as Name,
Test.value('(#Surname)[1]', 'varchar(20)') as Surname
FROM #Input.nodes('/Family/*') AS Tbl(Test)
OPTION ( OPTIMIZE FOR ( #Input = NULL ) )
DECLARE #xml XML = '
<Family>
<Main Surname="Smith" />
<Person Name="Fred" />
<Person Name="Jane" />
</Family>'
SELECT
RowNum = ROW_NUMBER() OVER (ORDER BY 1/0)
, t.c.value('#Name', 'VARCHAR(20)')
, t2.c2.value('#Surname', 'VARCHAR(20)')
FROM #xml.nodes('Family/Person') t(c)
CROSS APPLY t.c.nodes('../Main') t2(c2)
results -
-------------------- -------------------- --------------------
1 Fred Smith
2 Jane Smith
Related
I'm trying to convert a XML to a SQL Server table.
The XML is the following (sample):
declare #XML as XML
set #XML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:nomina12="http://www.sat.gob.mx/nomina12" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Fecha="2020-02-15">
<cfdi:Emisor Rfc="12345" />
<cfdi:Conceptos>
<cfdi:Concepto ClaveProdServ="84111505" Cantidad="1" />
</cfdi:Conceptos>
<cfdi:Complemento>
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/timbrefiscaldigital/TimbreFiscalDigitalv11.xsd"
Version="1.1" UUID="28C9D7DC-A9CB-4663-9606-3B7E37D922D4" FechaTimbrado="2020-04-15T18:59:18" />
<nomina12:Nomina Version="1.2" TipoNomina="O" FechaPago="2020-04-15" FechaInicialPago="2020-04-01" FechaFinalPago="2020-04-15" >
<nomina12:Emisor RegistroPatronal="Z2935172101" />
<nomina12:Receptor Curp="XYZ" />
<nomina12:Percepciones TotalSueldos="6807.63" TotalGravado="5865.48" TotalExento="942.15">
<nomina12:Percepcion TipoPercepcion="001" Clave="P001" Concepto="Sueldo Normal" ImporteGravado="4887.90" ImporteExento="0.00" />
<nomina12:Percepcion TipoPercepcion="005" Clave="P013" Concepto="Fondo Ahorro Empresa" ImporteGravado="0.00" ImporteExento="342.15" />
</nomina12:Percepciones>
<nomina12:Deducciones TotalOtrasDeducciones="1282.01" TotalImpuestosRetenidos="610.94">
<nomina12:Deduccion TipoDeduccion="001" Clave="D003" Concepto="IMSS" Importe="70.40" />
<nomina12:Deduccion TipoDeduccion="001" Clave="D034" Concepto="Cuota IMSS RCV" Importe="58.66" />
</nomina12:Deducciones>
</nomina12:Nomina>
</cfdi:Complemento>
</cfdi:Comprobante>'
My SQL code is the following:
;WITH XMLNAMESPACES(
'http://www.sat.gob.mx/cfd/3' as cfdi,
'http://www.w3.org/2001/XMLSchema-instance' as xsi,
'http://www.sat.gob.mx/TimbreFiscalDigital' as tfd,
'http://www.sat.gob.mx/TimbreFiscalDigital' as schemaLocation,
'http://www.sat.gob.mx/nomina12' as nomina12)
SELECT
xmldata.value('(#Fecha)', 'varchar(20)') AS fecha_elaboracion,
xmldata.value('(cfdi:Emisor/#Rfc)[1]', 'varchar(20)') AS rfc_emisor,
xmldata1.value('(#UUID)', 'varchar(100)') AS UUID,
xmldata1.value('(#FechaTimbrado)', 'varchar(50)') AS fecha_timbre,
xmldata2.value('(#FechaPago)', 'nvarchar(50)') AS fecha_pago,
xmldata2.value('(#FechaInicialPago)', 'nvarchar(50)') AS fecha_inicio,
xmldata2.value('(#FechaFinalPago)', 'nvarchar(50)') AS fecha_final
FROM
(SELECT #XML AS x) AS x1
CROSS APPLY
x.nodes('/cfdi:Comprobante') AS a(xmldata)
CROSS APPLY
xmldata.nodes('cfdi:Complemento/tfd:TimbreFiscalDigital') AS a1(xmldata1)
OUTER APPLY
xmldata.nodes('cfdi:Complemento/tfd:TimbreFiscalDigital/nomina12:Nomina') AS a2(xmldata2);
I'm getting information for the first nodes but when I want to get the node "nomina12:Nomina", I got a null VALUE, some idea what's wrong?
Thanks in advance for your help.
Somewhat cleaned up version.
I removed not needed namespace declarations, and adjusted proper data types.
SQL
DECLARE #xml XML =
N'<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3"
xmlns:nomina12="http://www.sat.gob.mx/nomina12"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Fecha="2020-02-15">
<cfdi:Emisor Rfc="12345"/>
<cfdi:Conceptos>
<cfdi:Concepto ClaveProdServ="84111505" Cantidad="1"/>
</cfdi:Conceptos>
<cfdi:Complemento>
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/timbrefiscaldigital/TimbreFiscalDigitalv11.xsd"
Version="1.1"
UUID="28C9D7DC-A9CB-4663-9606-3B7E37D922D4"
FechaTimbrado="2020-04-15T18:59:18"/>
<nomina12:Nomina Version="1.2" TipoNomina="O" FechaPago="2020-04-15"
FechaInicialPago="2020-04-01"
FechaFinalPago="2020-04-15">
<nomina12:Emisor RegistroPatronal="Z2935172101"/>
<nomina12:Receptor Curp="XYZ"/>
<nomina12:Percepciones TotalSueldos="6807.63" TotalGravado="5865.48"
TotalExento="942.15">
<nomina12:Percepcion TipoPercepcion="001" Clave="P001"
Concepto="Sueldo Normal"
ImporteGravado="4887.90"
ImporteExento="0.00"/>
<nomina12:Percepcion TipoPercepcion="005" Clave="P013"
Concepto="Fondo Ahorro Empresa"
ImporteGravado="0.00"
ImporteExento="342.15"/>
</nomina12:Percepciones>
<nomina12:Deducciones TotalOtrasDeducciones="1282.01"
TotalImpuestosRetenidos="610.94">
<nomina12:Deduccion TipoDeduccion="001" Clave="D003"
Concepto="IMSS" Importe="70.40"/>
<nomina12:Deduccion TipoDeduccion="001" Clave="D034"
Concepto="Cuota IMSS RCV" Importe="58.66"/>
</nomina12:Deducciones>
</nomina12:Nomina>
</cfdi:Complemento>
</cfdi:Comprobante>';
;WITH XMLNAMESPACES('http://www.sat.gob.mx/cfd/3' as cfdi
, 'http://www.sat.gob.mx/TimbreFiscalDigital' as tfd
, 'http://www.sat.gob.mx/nomina12' as nomina12)
SELECT xmldata.value('#Fecha', 'DATE') AS fecha_elaboracion
, xmldata.value('(cfdi:Emisor/#Rfc)[1]', 'VARCHAR(20)') AS rfc_emisor
, xmldata1.value('#UUID', 'UNIQUEIDENTIFIER') AS UUID
, xmldata1.value('#FechaTimbrado', 'VARCHAR(50)') AS fecha_timbre
, xmldata2.value('#FechaPago', 'DATE') AS fecha_pago
, xmldata2.value('#FechaInicialPago', 'DATE') AS fecha_inicio
, xmldata2.value('#FechaFinalPago', 'DATE') AS fecha_final
FROM #xml.nodes('/cfdi:Comprobante') AS a(xmldata)
OUTER APPLY xmldata.nodes('cfdi:Complemento/tfd:TimbreFiscalDigital') AS a1(xmldata1)
OUTER APPLY xmldata.nodes('cfdi:Complemento/nomina12:Nomina') AS a2(xmldata2);
Output
+-------------------+------------+--------------------------------------+---------------------+------------+--------------+-------------+
| fecha_elaboracion | rfc_emisor | UUID | fecha_timbre | fecha_pago | fecha_inicio | fecha_final |
+-------------------+------------+--------------------------------------+---------------------+------------+--------------+-------------+
| 2020-02-15 | 12345 | 28C9D7DC-A9CB-4663-9606-3B7E37D922D4 | 2020-04-15T18:59:18 | 2020-04-15 | 2020-04-01 | 2020-04-15 |
+-------------------+------------+--------------------------------------+---------------------+------------+--------------+-------------+
You are refering to the wrong path in your outer apply statement:
xmldata.nodes('cfdi:Complemento/nomina12:Nomina') AS a2(xmldata2);
And also here is the simplified version of your query:
WITH XMLNAMESPACES ('http://www.sat.gob.mx/cfd/3' AS cfdi
, 'http://www.w3.org/2001/XMLSchema-instance' AS xsi
, 'http://www.sat.gob.mx/TimbreFiscalDigital' AS tfd
, 'http://www.sat.gob.mx/TimbreFiscalDigital' AS schemaLocation
, 'http://www.sat.gob.mx/nomina12' AS nomina12
)
SELECT
xmldata.value('(/cfdi:Comprobante/#Fecha)[1]', 'varchar(20)') AS fecha_elaboracion
, xmldata.value('(/cfdi:Comprobante/cfdi:Emisor/#Rfc)[1]', 'varchar(20)') AS rfc_emisor
, xmldata.value('(/cfdi:Comprobante/cfdi:Complemento/tfd:TimbreFiscalDigital/#UUID)[1]', 'varchar(100)') AS UUID
, xmldata.value('(/cfdi:Comprobante/cfdi:Complemento/tfd:TimbreFiscalDigital/#FechaTimbrado)[1]', 'varchar(50)') AS fecha_timbre
, xmldata.value('(/cfdi:Comprobante/cfdi:Complemento/nomina12:Nomina/#FechaPago)[1]', 'nvarchar(50)') AS fecha_pago
, xmldata.value('(/cfdi:Comprobante/cfdi:Complemento/nomina12:Nomina/#FechaInicialPago)[1]', 'nvarchar(50)') AS fecha_inicio
, xmldata.value('(/cfdi:Comprobante/cfdi:Complemento/nomina12:Nomina/#FechaFinalPago)[1]', 'nvarchar(50)') AS fecha_final
FROM
(SELECT #XML AS xmldata) AS xmldata
I create table in oracle 12c like this
CREATE TABLE Test_xml
(
xml_data SYS.XMLTYPE
)
and insert XML data like this into column xml_data
<?xml version="1.0" encoding="UTF-8"?>
<persons xmlns="http://XXXXX.XXX/schema/1.0" name="ABC">
<person>
<name1 value="AAAA" />
<name2 value="CCCC" />
<name3 value="XXXX" />
</person>
<person>
<name1 value="11111" />
<name2 value="22222" />
<name3 value="33333" />
</person>
</persons>
but when I try to select data out from table by query
SELECT xt.*
FROM Test_xml x,
XMLTABLE('/persons/person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH 'name1/#value'
) xt;
not thing out from the query,How can i select name1 from the table?
You can provide the (default, in this case) namespace as another argument to XMLTable:
SELECT xt.*
FROM Test_xml x,
XMLTABLE(
xmlnamespaces(default 'http://XXXXX.XXX/schema/1.0'),
'/persons/person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH 'name1/#value'
) xt;
| NAME1 |
| :---- |
| AAAA |
| 11111 |
db<>fiddle showing your original query, #Sayan's wildcard version (which gets nulls for some reason, even though it works with a CTE), and this default namespace version.
Just replace /persons/person to /*:persons/*:person
SELECT/*+ NO_XML_QUERY_REWRITE */ xt.*
FROM Test_xml x,
XMLTABLE('/*:persons/*:person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH '*:name1/#value'
) xt;
Full example:
with test_xml(xml_data) as (
select xmltype(q'[<?xml version="1.0" encoding="UTF-8"?>
<persons xmlns="http://XXXXX.XXX/schema/1.0" name="ABC">
<person>
<name1 value="AAAA" />
<name2 value="CCCC" />
<name3 value="XXXX" />
</person>
<person>
<name1 value="11111" />
<name2 value="22222" />
<name3 value="33333" />
</person>
</persons>]'
)
from dual
)
SELECT xt.*
FROM Test_xml x,
XMLTABLE('/*:persons/*:person'
PASSING x.xml_data
COLUMNS
name1 VARCHAR2(2000) PATH '*:name1/#value'
) xt;
NAME1
--------------------
AAAA
11111
In SQL Server 2012, I have a table DEFINITIONS and a column in it that value of it is XML and name of column is "XML". I want to extract string "[CDATA[xxxx]]" from XML column (in parameter tag). how can I do that?
XML Example value:
<CatalogItem subType="0" type="82">
<id>7c75be9f-d490-4ff5-81ed-064349f4efde</id>
<name>Test White</name>
<description />
<attributes>0</attributes>
<parameters><conditions><condition grouping="or"><prop-key>usb_device_serial_number</prop-key><op-key>equals</op-key><value><![CDATA[gdefgd||]]></value></condition></conditions></parameters>
<customParameterNames />
<enforceProducts>
<Product type="WIN" />
</enforceProducts>
</CatalogItem>
Update 1
This Query solve my problem:
if object_id('dbo.cdata') is not null drop table dbo.cdata;
create table dbo.cdata(
x xml
);
insert into dbo.cdata(x)
SELECT XML FROM [HQ-MC-SRV-01].[dbo].[DEFINITIONS]
select
xv.v.value('.', 'nvarchar(max)') as cdata
from dbo.cdata
cross apply
cdata.x.nodes(
'//parameters'
) as pp(p)
cross apply (
values(
cast(pp.p.value(
'.', 'nvarchar(max)'
) as xml)
)
) as xp(p)
cross apply
xp.p.nodes('//value') as xv(v)
;
Thanks a Lot, Andrei Odegov
Update 2
i have to extract "serialNumber" and "endUser" from this XML with previous query too.
<CatalogItem subType="0" type="93">
<id>9d6825e6-fa89-44e1-8e0f-61768016624c</id>
<name>tteesstt</name>
<description />
<attributes>0</attributes>
<entries>
<entry>
<serialNumber>zsxxdx</serialNumber>
<userType>user</userType>
<endUser>farhad#drf.local</endUser>
<description />
</entry>
</entries>
</CatalogItem>
Update 3
<CatalogItem subType="0" type="12">
<id>169d6139-bdb6-4fff-88e6-09f00dd2f155</id>
<name>za</name>
<description />
<attributes>0</attributes>
<SIDMode>true</SIDMode>
<nameIdentificationType>1</nameIdentificationType>
<entries>
<entry>
<name>farhad</name>
<path>CN=farhad,CN=Users,DC=DCVLAB,DC=LOCAL</path>
<uid>F7A84834EAC8984AA7FBEAD7A5539432</uid>
<serverName>192.168.7.2</serverName>
<sid>S-1-5-21-907365782-933549064-1582919620-1103</sid>
<type>user</type>
<entry>
<name>IT-MNG</name>
<path>CN=IT,CN=Users,DC=sglab,DC=local</path>
<uid>BEDC7DDF93A8D54DAC7A5CEE7307BF4F</uid>
<serverName>192.168.7.201</serverName>
<sid>S-1-5-21-3362085216-3357124804-2073486349-1107</sid>
<type>user</type>
</entry>
</entries>
</CatalogItem>
Taking into account your test data, you can try to adapt the following code:
declare #x xml = '
<CatalogItem subType="0" type="82">
<id>7c75be9f-d490-4ff5-81ed-064349f4efde</id>
<name>Test White</name>
<description />
<attributes>0</attributes>
<parameters><conditions><condition grouping="or"><prop-key>usb_device_serial_number</prop-key><op-key>equals</op-key><value><![CDATA[gdefgd||]]></value></condition></conditions></parameters>
<customParameterNames />
<enforceProducts>
<Product type="WIN" />
</enforceProducts>
</CatalogItem>
';
select cast(#x.value('(//parameters)[1]', 'nvarchar(max)') as xml).query('//value/text()') as cdata;
Output
+----------+
| cdata |
+----------+
| gdefgd|| |
+----------+
Update:
if object_id('dbo.cdata') is not null drop table dbo.cdata;
create table dbo.cdata(
x xml
);
insert into dbo.cdata(x)
values ('
<CatalogItem subType="0" type="82">
<id>7c75be9f-d490-4ff5-81ed-064349f4efde</id>
<name>Test White</name>
<description />
<attributes>0</attributes>
<parameters><conditions><condition grouping="or"><prop-key>usb_device_serial_number</prop-key><op-key>equals</op-key><value><![CDATA[gdefgd||]]></value><value><![CDATA[foo]]></value></condition></conditions></parameters>
<parameters><conditions><condition grouping="or"><prop-key>usb_device_serial_number</prop-key><op-key>equals</op-key><value><![CDATA[gdefgd||170]]></value><value><![CDATA[foo170]]></value></condition></conditions></parameters>
<customParameterNames />
<enforceProducts>
<Product type="WIN" />
</enforceProducts>
</CatalogItem>
'), ('
<CatalogItem subType="0" type="82">
<id>7c75be9f-d490-4ff5-81ed-064349f4efde</id>
<name>Test White</name>
<description />
<attributes>0</attributes>
<parameters><conditions><condition grouping="or"><prop-key>usb_device_serial_number</prop-key><op-key>equals</op-key><value><![CDATA[gdefgd||42]]></value><value><![CDATA[foo42]]></value></condition></conditions></parameters>
<customParameterNames />
<enforceProducts>
<Product type="WIN" />
</enforceProducts>
</CatalogItem>
');
select
xv.v.value('.', 'nvarchar(max)') as cdata
from dbo.cdata
cross apply
cdata.x.nodes(
'//parameters'
) as pp(p)
cross apply (
values(
cast(pp.p.value(
'.', 'nvarchar(max)'
) as xml)
)
) as xp(p)
cross apply
xp.p.nodes('//value') as xv(v);
Update #2:
You can get more detail information about working in Sql Server with XML data in the "XQuery Language Reference (SQL Server)".There are two articles on the CROSS and OUTER APPLY operators with examples of their application to XML data.
select
replace(xv.v.value('.', 'nvarchar(max)'), '|', '') as cdata,
xci.ci.value('(//serialNumber)[1]', 'nvarchar(15)') as sn,
xci.ci.value('(entries/entry/endUser)[1]', 'nvarchar(50)') as eu,
xci.ci.value('(entries/entry/name)[1]', 'nvarchar(50)') as name
from dbo.cdata
cross apply
cdata.x.nodes('/CatalogItem') as xci(ci)
outer apply
xci.ci.nodes('parameters') as pp(p)
outer apply (
values(
cast(pp.p.value(
'.', 'nvarchar(max)'
) as xml)
)
) as xp(p)
outer apply
xp.p.nodes('//value') as xv(v);
Output:
+-----------+--------+------------------+--------+
| cdata | sn | eu | name |
+-----------+--------+------------------+--------+
| gdefgd | zsxxdx | farhad#drf.local | NULL |
| foo | zsxxdx | farhad#drf.local | NULL |
| gdefgd170 | zsxxdx | farhad#drf.local | NULL |
| foo170 | zsxxdx | farhad#drf.local | NULL |
| NULL | foobar | user#mail.net | NULL |
| NULL | NULL | NULL | farhad |
+-----------+--------+------------------+--------+
Test it online with Rextester.
i am looking to parse the XML string using SQL .I like to have the data in separate columns.could some one please help?.
The string:
<item id="1" value="0"></item><item id="2" value="14"></item><item id="0" value="0"></item>
This is how you can do it in SQL Server (e.g., v2008):
create table #temp (xml_data xml)
insert into #temp values ('<item id="1" value="0"></item><item id="2" value="14"></item><item id="0" value="0"></item>')
select C.value('#id', 'int') as [id]
,C.value('#value', 'int') as [value]
from #temp cross apply
#temp.xml_data.nodes('item') as X(C)
drop table #temp
Which returns:
id value
----------- -----------
1 0
2 14
0 0
Is it possible to achieve the following table from of output using XQuery in SQL Server
Edit: A change in my requirement, Consider i have a table which stores the below xml and also UserID
DECLARE #XML xml
set #XML = '<Security>
<FiscalYear Name="2012">
<Country Id="204">
<State Id="1">
<City Id="10"></City>
</State>
<State Id="2">
<City Id="20"></City>
<City Id="30"></City>
</State>
<State Id ="3"></State>
</Country >
</FiscalYear>
</Security>'
CREATE TABLE #tmp_user
(UserID INT,SecurityXML XML)
INSERT INTO #tmp_user
( UserID, SecurityXML )
VALUES ( 1,
#XML
)
Now how can i get a o/p like
Output:
UserID StateID CityID
1 1 10
1 2 20
1 2 30
1 3 0
Is it possible to achieve?
I modified your XML a bit because it was invalid. Change the end tag </Subsidiary> to </Country>.
declare #XML xml
set #XML =
'<Security>
<FiscalYear Name="2012">
<Country Id="204">
<State Id="1">
<City Id="10"></City>
</State>
<State Id="2">
<City Id="20"></City>
<City Id="30"></City>
</State>
<State Id ="3"></State>
</Country>
</FiscalYear>
</Security>'
select S.N.value('#Id', 'int') as StateID,
coalesce(C.N.value('#Id', 'int'), 0) as CityID
from #XML.nodes('/Security/FiscalYear/Country/State') as S(N)
outer apply S.N.nodes('City') as C(N)
A version using a table instead of XML variable
select T.UserID,
S.N.value('#Id', 'int') as StateID,
coalesce(C.N.value('#Id', 'int'), 0) as CityID
from #tmp_user as T
cross apply T.SecurityXML.nodes('/Security/FiscalYear/Country/State') as S(N)
outer apply S.N.nodes('City') as C(N)