I have a scenario where i have to use xml exist() function in where clause of the query. where i have to compare date from the xml with the current date. When i try to use GETDATE() function, i get below error.
The argument 1 of the XML data type method "exist" must be a string literal.
Please consider below query for your reference.
SELECT
TRY_CONVERT(datetime,NULLIF(t.x.value('(./Expire)[1]','varchar(max)'), '')) as expiration_date
FROM VW_Analytics_Base_Facts(nolock) BaseFact
CROSS APPLY BaseFact.Fact.nodes ('/Fact/Grant') t(x)
WHERE TYPE = '/asset/portfolio/option'
AND BaseFact.Fact.exist('./Expire[(text()[1] cast as xs:date?) le xs:date("' + CONVERT(NVARCHAR(max),CONVERT(date,GETDATE()))+'")]')=1
Thanks in advance.
Below is the updated working query.
DECLARE #tbl TABLE (XmlCol xml)
INSERT INTO #tbl VALUES
('<option>
<OptionName>Option 1</OptionName>
<grant>
<GrantName>Grant 1</GrantName>
<schedules>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>1/1/2018</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>2/1/2018</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>3/1/2018</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
</schedules>
</grant>
<grant>
<GrantName>Grant 2</GrantName>
<schedules>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>1/1/2019</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>2/1/2019</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>3/1/2019</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
</schedules>
</grant>
</option>'
)
SELECT e.XmlCol.value('(/option/OptionName)[1]', 'varchar(100)'),
t.x.value('../.././GrantName[1]','varchar(100)') GrantName,
t.x.value('(./scheduleDate)[1]', 'varchar(100)') scheduleDate
FROM #tbl e
CROSS APPLY (SELECT CONVERT(date,GETDATE())) dt(today)
cross apply e.XmlCol.nodes ('/option/grant/schedules/schedule') t(x)
WHERE e.XmlCol.exist('./scheduleDate[(text()[1] cast as xs:date?) le sql:column("dt.today")]')=1
i want to filter the record based on schedule date.
This query is a part of view so i cannot declare a variable.
Next time please append a stand-alone working sample with DDL, data inserts, your own code and the expected output. This would allow to test a solution.
In this case I have to guess and post untested:
SELECT
TRY_CONVERT(datetime,NULLIF(t.x.value('(./Expire)[1]','varchar(max)'), '')) as expiration_date
FROM VW_Analytics_Base_Facts(nolock) BaseFact
CROSS APPLY (SELECT CONVERT(date,GETDATE())) dt(today)
CROSS APPLY BaseFact.Fact.nodes ('/Fact/Grant') t(x)
WHERE TYPE = '/asset/portfolio/option'
AND BaseFact.Fact.exist('./Expire[(text()[1] cast as xs:date?) le sql:column("dt.today")]')=1;
This query will use one more CROSS APPLY in order to include the information in the resultset. The function sql:column() allows to use a result set's column within XQuery without breaking the must-be-a-literal rule (use sql:variable() to get hands on a declared variable's value).
UPDATE
Your idea to apply the filter as early as possible is not wrong. This was even better as predicate within .nodes(). But you have to rely on an implicit cast, something you should not do.
The expression (text()[1] cast as xs:date?) will use the system's culture. A value like 1/3/2018 might be taken as 1st of March or as 3rd of January. Therefore I'd suggest to read the value as string and use CONVERT with the appropriate style:
Btw: I use two times CROSS APPLY with .nodes() to avoid backward navigation (../../)
WITH DerivedTable AS
(
SELECT e.XmlCol.value('(/option/OptionName/text())[1]', 'varchar(100)') AS OptionName
,gr.value('(GrantName/text())[1]','varchar(100)') GrantName
,sch.value('(scheduleID/text())[1]', 'varchar(100)') scheduleID --use appropriate type, might be "int"
,sch.value('(scheduleName/text())[1]', 'varchar(100)') scheduleName
,CONVERT(DATE,sch.value('(scheduleDate/text())[1]', 'varchar(100)'),110) scheduleDate --fetch this as varchar, to avoid implicit casts
--use CONVERT with the appropriate style to get a real date
--110 is mdy, 103 is dmy
,sch.value('(scheduleAmount/text())[1]', 'varchar(100)') scheduleAmount --use appropriate type, might be "decimal(12,4)"
FROM #tbl e
CROSS APPLY e.XmlCol.nodes ('/option/grant') A(gr)
CROSS APPLY A.gr.nodes('schedules/schedule') B(sch)
)
SELECT dt.*
FROM DerivedTable dt
--use a simple WHERE here
This will return the XML's content as derived table. Use a simple WHERE to filter this derived table.
Below is the work around which worked for me.
SELECT e.XmlCol.value('(/option/OptionName)[1]', 'varchar(100)') OptionName,
t.x.value('../.././GrantName[1]','varchar(100)') GrantName,
t.x.value('(./scheduleDate)[1]', 'varchar(100)') scheduleDate
FROM #tbl e
CROSS APPLY e.XmlCol.nodes ('/option/grant/schedules/schedule') t(x)
WHERE TRY_CONVERT(datetime,NULLIF(t.x.value('(./scheduleDate)[1]','varchar(max)'), '')) BETWEEN CONVERT(date,GETDATE()) AND DATEADD(YEAR, 1, GETDATE())
Related
I want to get the values Total and TipoDeComprobante of the tag name cfdi:Comprobante, currently in my table the column where the XML is stored is of type image so I do the following query to get the value of the XML, I tried to do some substring but I have not been successful, could you help me?
Query to obtain the xml
select cast(cast(xml as varbinary(max)) as varchar(max)) as column_name
from [tb_cfdi]
where uuid = 'f425cd6d-ed30-4a0d-8135-8dc7229b79ff' ;
Result
<?xml version="1.0" encoding="utf-8"?>
<cfdi:Comprobante xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" Version="3.3" Serie="PRUEBAS-SEBASTIAN" Folio="SEBASTIANCANCELACIONSD-27" Fecha="2022-05-23T18:42:38" Sello="dUxav04E0XcbOhMsXjXDlWbFlZdcXqOk/R8DHLd3TILZ7gg5/aw+T+gI9yMLpVLvPkeEm5+A++QWHi0I/26TVTlsx6ejFWxXTEdewmarCuSrRO24CSFPz2dLX6ojy5Eg6QGTV0EQiS3LRtLsnTyqXH2QE7Ne+Y7cqXXEcXWZ63AIAkkWY4U9uQu1/3F0jiHFfMCf4lBuSgDQ8c8LniBKoAsFoTySZmyhJv4h2koz1bE0Yl6Rvrr0NNlmYSH+SGqbkYVsYKM9fbHIDJbNXosRqIGlugOlLIT2499j3gQfTn7d3sdQLtVqn4tcqaDGx2Du2dm1XFJRwT7aB62btNVW1Q==" FormaPago="99" NoCertificado="30001000000400002325" Certificado="MIIFdjCCA16gAwIBAgIUMzAwMDEwMDAwMDA0MDAwMDIzMjUwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWRpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMTkwNTI5MTgwNDM0WhcNMjMwNTI5MTgwNDM0WjCBnTEYMBYGA1UEAxMPWEFJTUUgV0VJUiBST0pPMRgwFgYDVQQpEw9YQUlNRSBXRUlSIFJPSk8xGDAWBgNVBAoTD1hBSU1FIFdFSVIgUk9KTzEWMBQGA1UELRMNV0VSWDYzMTAxNlMzMDEbMBkGA1UEBRMSV0VSWDYzMTAxNkhKQ1JKTTA4MRgwFgYDVQQLEw9YQUlNRSBXRUlSIFJPSk8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwSjRVq5oohPioYAWcUoxVAvudA2MqKDBwX/GukHngSLtqMZ6b5I/JIhnDyqauugRYaQRW7FH+jxoijWR340zYhhgea2HYfE7WDUQNlraqtZ9re+OX5RX3KQA4sPw3pcVMcUbiCDr3vVBVaWtEaAaIsh1LxPgwWQ2egFarmMkHHEUTLkISe2WqrOFKd3PJ9vgj6h4+EF0+C3qKt55pRsLTh9r828Mo7E7AROaSJK5XG/x/5xE5sYyV2kqCFIs11xdfTn00FEmUgK+caW2lKYj9KqSuKu8UyJcOdrk2LhZlHhJO16LXrTsItJZPhJJ03FYi4+w0TNY7Eg87BC38nz5NAgMBAAGjHTAbMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgbAMA0GCSqGSIb3DQEBCwUAA4ICAQCF9+3Q0bUnTVREjva/R2fNjnoksrHUwviYkRGOTfQeJbOF76gcz1aqkMpuhk9uvbnxLtNXTrpJZaW/9f7aVSIEP7x9u5XcikC5NOw4g0r/8uhCbeF41yIIAdCufg22YSMyfzAtzmW96tO1Q/PYaloCpEjtiWcufSVpmOFP/oQszWP8g4z3XG/DDPrhvO8Lk2ZghfsCuhi4KvNnBUZOn7Vm4kadt3QIfb4+gCkMn4uA0GLCIGWH/yvF4dQdc6if1ev/1FtGHe7CP54e+PuOm8JjBFkzfXNiXIZq8NdnUNnwEUl+zeXm1H4qog4WC/sceOtUVbRQy9sUYeX13+1yNtRQxGMebD+tsTvY4xtwqjMw7TeOcLe27rJsPAV62vFIsL8HlDuYNC3mZJzg40pxlPjqt+xhQAXM3MOYfQq+89t+4Cr4xaglLZ2NHAY7MZk0SN62c7f8yL4EA81Yg6LeIgDei3ZiNXcLeFKtCvDwbuIoD9Oqane7QH/jfOrIlRRxHeGb7Nmob+PXe7BN9VYqQ0/dOSPRKol0DLRRpwA/2Qn8nbq3nl0ZCbJYfC/8psh/hUAkPUXPRH2+JoWORVzKjBDZ5FaLxV9t5q3rV/YeMVuS9qqzjs1/RvM5aa4cLh3Cc6S1vojBy1QitiGwRxzy4SHkF69dyxnQbyUVOMkuBiMyWg==" SubTotal="200.0" Moneda="AMD" TipoCambio="1" Total="200.16" TipoDeComprobante="I" MetodoPago="PPD" LugarExpedicion="20000" xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd">
<cfdi:Emisor Rfc="WERX631016S30" Nombre="XAIME WEIR ROJO" RegimenFiscal="605"/>
<cfdi:Receptor Rfc="EKU9003173C9" Nombre="ESCUELA KEMPER URGATE" UsoCFDI="G03"/>
<cfdi:Conceptos>
<cfdi:Concepto ClaveProdServ="50211503" Cantidad="1" ClaveUnidad="H87" Unidad="Pieza" Descripcion="Cigarros" ValorUnitario="200.0" Importe="200.0">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="1" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="0.16"/>
</cfdi:Traslados>
<cfdi:Retenciones>
<cfdi:Retencion Base="1" Impuesto="001" TipoFactor="Tasa" TasaOCuota="0.100000" Importe="0.00"/>
<cfdi:Retencion Base="1" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.106666" Importe="0.00"/>
</cfdi:Retenciones>
</cfdi:Impuestos>
</cfdi:Concepto>
</cfdi:Conceptos>
<cfdi:Impuestos TotalImpuestosRetenidos="0.00" TotalImpuestosTrasladados="0.16">
<cfdi:Retenciones>
<cfdi:Retencion Impuesto="001" Importe="0.00"/>
<cfdi:Retencion Impuesto="002" Importe="0.00"/>
</cfdi:Retenciones>
<cfdi:Traslados>
<cfdi:Traslado Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="0.16"/>
</cfdi:Traslados>
</cfdi:Impuestos>
<cfdi:Complemento>
<tfd:TimbreFiscalDigital xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigitalv11.xsd" Version="1.1" UUID="f425cd6d-ed30-4a0d-8135-8dc7229b79ff" FechaTimbrado="2022-05-23T18:42:50" RfcProvCertif="SPR190613I52" SelloCFD="dUxav04E0XcbOhMsXjXDlWbFlZdcXqOk/R8DHLd3TILZ7gg5/aw+T+gI9yMLpVLvPkeEm5+A++QWHi0I/26TVTlsx6ejFWxXTEdewmarCuSrRO24CSFPz2dLX6ojy5Eg6QGTV0EQiS3LRtLsnTyqXH2QE7Ne+Y7cqXXEcXWZ63AIAkkWY4U9uQu1/3F0jiHFfMCf4lBuSgDQ8c8LniBKoAsFoTySZmyhJv4h2koz1bE0Yl6Rvrr0NNlmYSH+SGqbkYVsYKM9fbHIDJbNXosRqIGlugOlLIT2499j3gQfTn7d3sdQLtVqn4tcqaDGx2Du2dm1XFJRwT7aB62btNVW1Q==" NoCertificadoSAT="30001000000400002495" SelloSAT="gfFwozFJpvd1zpwvQPCkUWBfBG72/bn1+0CwrgnPx466uzjrL0RMsoSRHplN4dpTiqfAT/+bhGA8KjDDG6p+3RvlVxx1dmUIVffRcTv2Jd1D+zZQRlt3RkHuANjCtOSVZKiW181WUyqzmH7ehL+S7lyBL7odWswW4CGr5UPrMBFaSDSR6K+3aw2nd7SJce/2+rWN4szUIE4YlUS2whPLznlOdsRrSSy6OHJLkptSJaGn86F8bcp46Idz4lDNkI1G72lN3Kmat5cTrA8f9VTj8BfCzlmKdXg/jD7/1vOHm4mF3qmaLUym84Yj370ax7dGxYb4KoNLAxuYRJjJq1wkbw==" xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</cfdi:Complemento>
</cfdi:Comprobante>
You can also use #marc_s's answer with your existing data type, although I agree the table should be changed if at all possible
WITH XMLNAMESPACES('http://www.sat.gob.mx/cfd/3' AS cfdi)
SELECT
Total = xc.value('#Total' , 'decimal(20,2)'),
TipoDeComprobante = xc.value('#TipoDeComprobante', 'varchar(20)')
FROM tb_cfdi c
CROSS APPLY (
SELECT CAST(CAST(xml AS varbinary(max)) AS xml) AS data
) v
CROSS APPLY v.data.nodes('/cfdi:Comprobante') AS xt(xc)
WHERE c.uuid = 'f425cd6d-ed30-4a0d-8135-8dc7229b79ff';
First of all - get rid of that Image datatype! It's deprecated and will be removed in a future version of SQL Server.
Image is also a binary type - why use that to store textual information like XML ?? Makes no sense.... use XML datatype for best results.
Once you do have your XML in a T-SQL variable #data XML, you can then use this XQuery to get the values you're interested in:
WITH XMLNAMESPACES('http://www.sat.gob.mx/cfd/3' AS cfdi)
SELECT
Total = xc.value('#Total', 'decimal(20,2)'),
TipoDeComprobante = xc.value('#TipoDeComprobante', 'varchar(20)')
FROM
#data.nodes('(/cfdi:Comprobante)') AS XT(XC)
I'm having trouble extracting some data from a XML file to MSSQL DB using CROSS APPLY, here is what I have:
Note: I did read about how to post here but pardon me if I'm still wrong.
XML File:
<?xml version="1.0" encoding="UTF-8" ?>
<ZMPROD01>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
<DOCNUM>0000003899888135</DOCNUM>
<CREDAT>20220201</CREDAT>
<CRETIM>152041</CRETIM>
</EDI_DC40>
<ZPROD SEGMENT="1">
<WERKS>8285</WERKS>
<LGNUM>0</LGNUM>
<AUFNR>000915229446</AUFNR>
<LINENO>RM01PL01</LINENO>
<CHARG>0006186588</CHARG>
<START1>20220202</START1>
<START2>211609</START2>
<QTY>4166.000</QTY>
<END1>20220202</END1>
<END2>240000</END2>
<MAKTX>579 FUS5 75ML ULTRA SENST GEL</MAKTX>
<PLN_ORDER>6963701111</PLN_ORDER>
<Z1PRODI SEGMENT="1">
<POSNR>000010</POSNR>
<MATNR>000000000098920665</MATNR>
</Z1PRODI>
<Z1PRODI SEGMENT="1">
<POSNR>000040</POSNR>
<HRKFT>V010</HRKFT>
</Z1PRODI>
<Z1PRODI SEGMENT="1">
<POSNR>000050</POSNR>
<MATNR>000000000099396964</MATNR>
</Z1PRODI>
</ZPROD>
</IDOC>
</ZMPROD01>
My SQL query:
INSERT INTO XMLTESTTABLE(PONo, ASP, LOTNo, EntryDate, StartDate, EndDate, GAS, PlannedQty, LineNum, SAPDesc, StartTime, EndTime)
SELECT
MY_XML.ZPROD.query('AUFNR').value('.', 'VARCHAR(9)'),
MY_XML.ZPROD.query('CHARG').value('.', 'VARCHAR(8)'),
MY_XML.ZPROD.query('PLN_ORDER').value('.', 'VARCHAR(10)'),
MY_XML.ZPROD.query('START1').value('.', 'date'),
MY_XML.ZPROD.query('START1').value('.', 'date'),
MY_XML.ZPROD.query('END1').value('.', 'date'),
MY_XML.ZPROD.query('CHARG').value('.', 'VARCHAR(8)'),
MY_XML.ZPROD.query('QTY').value('.', 'VARCHAR(9)'),
MY_XML.ZPROD.query('LINENO').value('.', 'VARCHAR(1)'),
MY_XML.ZPROD.query('MAKTX').value('.', 'VARCHAR(9)'),
MY_XML.ZPROD.query('START2').value('.', 'time'),
MY_XML.ZPROD.query('END2').value('.', 'time')
FROM (SELECT CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'C:\Users\PC_user\Documents\Idoc3899888135.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('ZMPROD01/ZPROD') AS MY_XML (ZPROD);
The result I get is (0 rows affected). I tried replacing ZMPROD01/ZPROD several times but different errors appeared, at some point complained about being unable to convert to date datatype.
On a separate note how, can I get data from EDI_DC40 as well? Not sure how the CROSS APPLY would look like to look for different places on the document.
Your help is appreciated.
Thanks.
Please try the following solution.
Notable points:
I adjusted the XPath expression in the .nodes() method.
No need to use the .query() method.
text() is added for performance reasons.
Last two data elements converted into TIME data type.
As it was already mentioned, the <END2>240000</END2> is not a legit
value for the TIME data type.
SQL
--INSERT INTO XMLTESTTABLE(PONo, ASP, LOTNo, EntryDate, StartDate, EndDate, GAS, PlannedQty, LineNum, SAPDesc, StartTime, EndTime)
SELECT ZPROD.value('(AUFNR/text())[1]', 'VARCHAR(9)')
, ZPROD.value('(CHARG/text())[1]', 'VARCHAR(8)')
, ZPROD.value('(PLN_ORDER/text())[1]', 'VARCHAR(10)')
, ZPROD.value('(START1/text())[1]', 'date')
, ZPROD.value('(START1/text())[1]', 'date')
, ZPROD.value('(END1/text())[1]', 'date')
, ZPROD.value('(CHARG/text())[1]', 'VARCHAR(8)')
, ZPROD.value('(QTY/text())[1]', 'VARCHAR(9)')
, ZPROD.value('(LINENO/text())[1]', 'VARCHAR(1)')
, ZPROD.value('(MAKTX/text())[1]', 'VARCHAR(9)')
, TRY_CAST(STUFF(STUFF(ZPROD.value('(START2/text())[1]', 'CHAR(6)'),3,0,':'),6,0,':') AS TIME)
, TRY_CAST(STUFF(STUFF(ZPROD.value('(END2/text())[1]', 'CHAR(6)'),3,0,':'),6,0,':') AS TIME)
FROM (SELECT CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'e:\Temp\Idoc3899888135.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('/ZMPROD01/IDOC/ZPROD') AS MY_XML (ZPROD);
Your path seems wrong - what about:
CROSS APPLY MY_XML.nodes('ZMPROD01/IDOC/ZPROD') AS MY_XML (ZPROD);
You seem to have forgotten the IDOC
And the data type is because you cannot convert the times to the data type time. Try starting with the select where everything is converted to varchar, then start changing the data tyoes, and you will find the errors
SELECT CAST('211609' AS TIME)
returns error
I have the following XML file (I put here only a short scheme), it's a SEPA XML bank statement. I'm not familiar with parsing XML files, my next move will be inserting and comparing to data stored in the SQL databases for error checks. Sadly, I know what to do next, don't know how to make progress with my first step. All I need is to create a table to select values of 2 attributes from a file stored at a particular place
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
<BkToCstmrStmt>
<GrpHdr>
..........
</GrpHdr>
<Stmt>
<Ntry>
<Amt Ccy="EUR">RequestedAmount1</Amt>
<AddtlNtryInf>RequestedInfo1</AddtlNtryInf>
</Ntry>
<Ntry>
<Amt Ccy="EUR">RequestedAmount2</Amt>
<AddtlNtryInf>RequestedInfo2</AddtlNtryInf>
</Ntry>
<Ntry>
<Amt Ccy="EUR">RequestedAmount3</Amt>
<AddtlNtryInf>RequestedInfo3</AddtlNtryInf>
</Ntry>
</Stmt>
</BkToCstmrStmt>
</Document>
If the XML structure was simpler, for example like this...
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Ntry>
<Amt Ccy="EUR">RequestedAmount1</Amt>
<AddtlNtryInf>RequestedInfo1</AddtlNtryInf>
</Ntry>
<Ntry>
<Amt Ccy="EUR">RequestedAmount2</Amt>
<AddtlNtryInf>RequestedInfo2</AddtlNtryInf>
</Ntry>
<Ntry>
<Amt Ccy="EUR">RequestedAmount3</Amt>
<AddtlNtryInf>RequestedInfo3</AddtlNtryInf>
</Ntry>
...then I'd use this query to select requested attributes Amt and AddtlNtryInf and it works perfectly
SELECT
MY_XML.Ntry.query('Amt').value('.', 'NVARCHAR(255)') AS Amt,
MY_XML.Ntry.query('AddtlNtryInf').value('.', 'NVARCHAR(255)') AS AddtlNtryInf
FROM (SELECT CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'C:\tmp\TestSqlSimple.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('Ntry') AS MY_XML (Ntry);
But don't know how to deal with that more complicated one. I've tried something like this and several similar attempts but I failed because it doesn't select anything, the result is nothing
SELECT
MY_XML.Ntry.query('Amt').value('.', 'NVARCHAR(255)') AS Amt,
MY_XML.Ntry.query('AddtlNtryInf').value('.', 'NVARCHAR(255)') AS AddtlNtryInf
FROM (SELECT CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'C:\tmp\TestSqlSimple.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('/Document/BkToCstmrStmt/Stmt/Ntry') AS MY_XML (Ntry);
Can't figure out what to do with that CROSS APPLY. Thank you very much for any suggestions or improvements, you're doing a great job
You were almost there.
(1) The XML file has a default namespace, and it needs a special treatment via XMLNAMESPACES clause.
(2) The Amt element probably has a numeric value so you could use DECIMAL(x,y) data type. But I kept the NVARCHAR(255) to match the obfuscated XML file example.
(3) The SQL below is using .value() method without unnecessary .query() method.
(4) It is a good practice to use elementName/text() technique for performance reasons. It is MS SQL Server specific peculiarity.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (
ID INT IDENTITY PRIMARY KEY,
Amt NVARCHAR(255),
AddtlNtryInf NVARCHAR(255)
);
-- DDL and sample data population, end
;WITH XMLNAMESPACES (DEFAULT 'urn:iso:std:iso:20022:tech:xsd:camt.053.001.02')
, XmlFile (xmlData) AS
(
SELECT TRY_CAST(BulkColumn AS XML)
FROM OPENROWSET(BULK 'e:\Temp\TestSqlSimple.xml', CODEPAGE = '65001', SINGLE_BLOB) AS x
)
INSERT INTO #tbl (Amt, AddtlNtryInf)
SELECT c.value('(Amt/text())[1]', 'NVARCHAR(255)') AS Amt
, c.value('(AddtlNtryInf/text())[1]', 'NVARCHAR(255)') AS AddtlNtryInf
FROM XmlFile CROSS APPLY xmlData.nodes('/Document/BkToCstmrStmt/Stmt/Ntry') AS t(c);
-- test
SELECT * FROM #tbl;
Something like this:
declare #doc xml = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
<BkToCstmrStmt>
<GrpHdr>
..........
</GrpHdr>
<Stmt>
<Ntry>
<Amt Ccy="EUR">RequestedAmount1</Amt>
<AddtlNtryInf>RequestedInfo1</AddtlNtryInf>
</Ntry>
<Ntry>
<Amt Ccy="EUR">RequestedAmount2</Amt>
<AddtlNtryInf>RequestedInfo2</AddtlNtryInf>
</Ntry>
<Ntry>
<Amt Ccy="EUR">RequestedAmount3</Amt>
<AddtlNtryInf>RequestedInfo3</AddtlNtryInf>
</Ntry>
</Stmt>
</BkToCstmrStmt>
</Document>';
with xmlnamespaces (DEFAULT 'urn:iso:std:iso:20022:tech:xsd:camt.053.001.02')
select s.Stmt.value('(GrpHdr)[1]', 'varchar(200)') GrpHdr,
n.Ntry.value('(Amt)[1]', 'varchar(200)') Amt,
n.Ntry.value('(AddtlNtryInf)[1]', 'varchar(200)') AddtlNtryInf
from #doc.nodes('/Document/BkToCstmrStmt') s(Stmt)
outer apply s.Stmt.nodes('Stmt/Ntry') n(Ntry)
Here is a my SQL:
create table sqm (data xml)
insert into sqm
select '<DataSet xmlns="http://www.bnr.ro/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bnr.ro/xsd nbrfxrates.xsd">
<Cube date="2017-06-30">
<Rate currency="AED">1.0867</Rate>
<Rate currency="AUD">3.0665</Rate>
<Rate currency="BGN">2.3284</Rate>
</Cube>
</DataSet>'
select
m.c.value('#date', 'date') as valuta
from sqm as s
outer apply s.data.nodes('/DataSet/Body/Cube') as m(c)
After spending hours trying to find out why my SQL kept returning NULL in the db, I discovered that my problem was due to Hyperlink references at the very beginning of the XML(after DataSet).
I really want to know why is this happening and who can I delete everything between <DataSet end >. Is there any other option?
if you can think of a better title, please edit.
You need to declare the namespace using WITH XMLNAMESPACES
Also your example XML has no Body element so I removed that from the Xpath.
Demo
WITH XMLNAMESPACES (DEFAULT 'http://www.bnr.ro/xsd')
select
m.c.value('#date', 'date') as valuta
from sqm as s
outer apply s.data.nodes('/DataSet/Cube') as m(c)
Or alternatively you can use
select
m.c.value('#date', 'date') as valuta
from sqm as s
outer apply s.data.nodes('/*:DataSet/*:Cube') as m(c)
I was wondering which one of the following two was the better way of filtering on a column in my xml variable.
INSERT INTO [Tracking].[Team]([Name], [Description], [Begin_Dt], [End_Dt], [Update_Dt])
SELECT
x.[Team].value('Name', 'varchar(100)') AS [Name],
x.[Team].value('Description', 'varchar(250)') AS [Description],
x.[Team].value('Begin_Dt', 'datetime') AS [Begin_Dt],
x.[Team].value('End_Dt', 'datetime') AS [End_Dt],
getdate() as [Update_Dt]
FROM #xml.nodes('/Team') x([Team])
WHERE x.[Team].value('Team_Ref_ID', 'int') = 0
INSERT INTO [Tracking].[Team]([Name], [Description], [Begin_Dt], [End_Dt], [Update_Dt])
SELECT
x.[Team].value('Name', 'varchar(100)') AS [Name],
x.[Team].value('Description', 'varchar(250)') AS [Description],
x.[Team].value('Begin_Dt', 'datetime') AS [Begin_Dt],
x.[Team].value('End_Dt', 'datetime') AS [End_Dt],
getdate() as [Update_Dt]
FROM #xml.nodes('/Team') x([Team])
WHERE x.[Team].exist('Team_Ref_ID[. = 0]') = 1
Notice the WHERE clause, one uses exist, the other uses value, or is there a third method that's more effective ?
Thanks,
Raul
For querying an #xml variable the best improvement you can do is to have it be of type with an XML schema, see Typed XML Compared to Untyped XML. This will benefit the XPath expressions in your query (the .nodes('/Team'), the various .value and the .exists operators). But there will be an upfront cost in validating the schema when the variable is assigned.
The other typical XML performance improvement is XML indexes, but unfortunately they cannot be applied to variables so the point is mute in regard to your problem.
As to your particular minutia question (whether the .value('Team_Ref_ID') is faster than .exists('Team_Ref_ID[. = 0]')) I think the former is faster, but I have no evidence and I may well be wrong.
Actually what i found to be better was this
INSERT INTO [Tracking].[Team]([Name], [Description], [Begin_Dt], [End_Dt], [Update_Dt])
SELECT
x.[Team].value('Name', 'varchar(100)') AS [Name],
x.[Team].value('Description', 'varchar(250)') AS [Description],
x.[Team].value('Begin_Dt', 'datetime') AS [Begin_Dt],
x.[Team].value('End_Dt', 'datetime') AS [End_Dt],
getdate() as [Update_Dt]
FROM #xml.nodes('/Team[Team_Ref_Id = 0]') x([Team])
It filters out the nodes in the xml parser instead of having to filter them out in the SQL.