I have a sql server 2008 r2 database that contains a table with raw XML in one of the columns. I have written a query to extract data from this table using group by and stuff to concatenate all of the columns that meet a 2 column group by clause. The issue is that when I stuff the columns I get duplicate XML declarations and the XML is not valid.
<?xml version="1.0" encoding="UTF-8"?>
<Fruits>
<Fruit Type="Lemons" Price="0.50" />
<Fruit Type="Apples" Price="0.75" />
</Fruits>
<?xml version="1.0" encoding="UTF-8"?>
<Fruits>
<Fruit Type="Cherries" Price="0.10" />
<Fruit Type="Dates" Price="0.25" />
</Fruits>
I looked at adding a substring call to remove this declaration but the query is getting quite complex and slow. I am working with an XSLT transformer that creates XML output. In the end I have many rows that can be delivered to a common destination. I basically need a way to concatenate the XML while removing the duplicate XML declarations and handling the XML hierarchy
This would be converted to
<?xml version="1.0" encoding="UTF-8"?>
<Fruits>
<Fruit Type="Lemons" Price="0.50" />
<Fruit Type="Apples" Price="0.75" />
<Fruit Type="Cherries" Price="0.10" />
<Fruit Type="Dates" Price="0.25" />
</Fruits>
Does anyone know how to do this in a systematic way? I am not actually working with fruit but used it as a simplified example.
Here is the actual query
SELECT tt.ProductCodeID, tt.ProviderID, tt.ContentXML, LEN(tt.ContentXML) AS xmllength from
(SELECT p.ProductCodeID, l.ProviderID,
STUFF((
SELECT pl1.Content FROM dbo.Payload pl1
INNER JOIN Log l1 ON pl1.LogID=l1.LogID
INNER JOIN Provider p1 ON l1.ProviderID=p1.ProviderID
WHERE pl1.ProductCodeID=p.ProductCodeID AND
pl1.PayloadTypeID=pt.PayloadTypeID
AND p1.ProviderID=l.ProviderID
FOR XML PATH(''), TYPE).value('.','varchar(max)'),1,1,'') AS ContentXML
FROM dbo.Queue q
INNER JOIN log l ON q.LogID=l.LogID
INNER JOIN payload pl ON l.LogID=pl.LogID
INNER JOIN dbo.PayloadType pt ON pl.PayloadTypeID = pt.PayloadTypeID
INNER JOIN dbo.ProductCode p ON pl.ProductCodeID= p.ProductCodeID
INNER JOIN dbo.Status s ON q.StatusID=s.StatusID
WHERE s.Name = 'processed' AND pt.Name='MIF'
GROUP BY p.ProductCodeID, l.ProviderID, pt.PayloadTypeID) AS tt
Thank you
It's ugly, but if you always know what the objects are going to be you could strip out the xml you don't want.
SELECT ..., '<?xml version="1.0" encoding="UTF-8"?><Fruits>' +
REPLACE( REPLACE(xml_column,
'<?xml version="1.0" encoding="UTF-8"?><Fruits>',
''),
'</Fruits',
'') +
'</Fruits>
Or, use a similar step to preprocess the data, and use the replace function to add a column missing the xml headers.
This one is the quick solution that I can think of right now.
CREATE TABLE Test2(ID INT,Col1 XML, Col2 XML)
INSERT INTO Test2 VALUES(1,'<?xml version="1.0" encoding="UTF-8"?><Fruits><Fruit Type="Lemons" Price="0.10" /><Fruit Type="Apples" Price="0.25" /></Fruits>','<?xml version="1.0" encoding="UTF-8"?><Fruits><Fruit Type="Cherries" Price="0.10" /><Fruit Type="Dates" Price="0.25" /></Fruits>')
INSERT INTO Test2 VALUES(2,'<?xml version="1.0" encoding="UTF-8"?><Fruits><Fruit Type="Lemons2" Price="0.10" /><Fruit Type="Apples2" Price="0.25" /></Fruits>','<?xml version="1.0" encoding="UTF-8"?><Fruits><Fruit Type="Cherries2" Price="0.10" /><Fruit Type="Dates3" Price="0.25" /></Fruits>')
SELECT ID, (
SELECT Fruit,Price FROM(
SELECT
Tbl.Col.value('#Type', 'varchar(100)') AS Fruit,
Tbl.Col.value('#Price', 'varchar(10)') AS Price
FROM Col1.nodes('//Fruit') Tbl(Col)
UNION
SELECT
Tbl.Col.value('#Type', 'varchar(100)'),
Tbl.Col.value('#Price', 'varchar(10)')
FROM Col2.nodes('//Fruit') Tbl(Col)) Fruits FOR XML AUTO
) FROM Test2
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've been trying to import the field GivenName in my example XML but for some reason it's not working, i've been using the following SQL query, i think i'm using the correct field and the correct nodes but i'm not 100% sure about the XMLNameSpaces
Thank you very much in advance for your help
This is the example SQL Query i'm using:
DECLARE #xml XML = (SELECT [Xml] FROM ExampleTable)
;WITH XMLNAMESPACES (DEFAULT 'http://www.opentravel.org/OTA/2003/05','http://www.w3.org/2003/05/soap-envelope' )
select FirstName = ProfileInfo.value('Profiles[1]/ProfileInfo[1]/Profile[1]/Customer[1]/PersonName[1]/#GivenName', 'nvarchar(255)')
FROM #xml.nodes('Envelope/Body/OTA_Example/Info/Infos/ResUser') as T1(Profiles)
outer apply T1.Profiles.nodes('ResUser2') as T2(ProfileInfo)
This is the example XML i'm using for the import:
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<soap2:Header xmlns:htng="http://htng.org/1.3/Header/" xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:wss="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:htnga="http://htng.org/PWSWG/2007/02/AsyncHeaders"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap2="http://www.w3.org/2003/05/soap-envelope">
<wsa:Action>Example</wsa:Action>
<wsa:ReplyTo>
<wsa:Address>Example2</wsa:Address>
</wsa:ReplyTo>
<htnga:ReplyTo>
<wsa:Address>Example3</wsa:Address>
</htnga:ReplyTo>
<wsa:MessageID>123</wsa:MessageID>
</soap2:Header>
<Body>
<OTA_Example xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05" Version="5.000" >
<Info>
<Infos CreateDateTime="2021-09-20T06:52:40" CreatorID="User">
<UniqueID Type="1" ID="12341251" />
<ResUser>
<ResUser2 ResGuestRPH="1" PrimaryIndicator="true">
<Profiles>
<ProfileInfo>
<Profile ProfileType="1">
<Customer>
<PersonName>
<NamePrefix>Mr.</NamePrefix>
<GivenName>FirstnameTest</GivenName>
<Surname>LastnameTest</Surname>
</PersonName>
</Customer>
</Profile>
</ProfileInfo>
</Profiles>
</ResUser2>
</ResUser>
</Infos>
</Info>
</OTA_Example>
</Body>
</Envelope>
GivenName is not an attribute, so you shouldn't use # for it.
It's unclear why you needed .nodes, it is only needed if there were multiple nodes that needed breaking out into separate rows
You can also select straight out of ExampleTable, you do not need to store it in a variable.
;WITH XMLNAMESPACES (
'http://www.w3.org/2003/05/soap-envelope' AS soap,
DEFAULT 'http://www.opentravel.org/OTA/2003/05')
select FirstName = [XML].value('(soap:Envelope/soap:Body/OTA_Example/Info/Infos/ResUser/ResUser2/Profiles/ProfileInfo/Profile/Customer/PersonName/GivenName/text())[1]', 'nvarchar(255)')
FROM ExampleTable
db<>fiddle
I have to get values of company node elements.I have tried all the
method to fetch data from the node but no luck. Below is my XML.
<?xml version="1.0"?>
<CompanyInvoice xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Customer xmlns="http://t.service/CompanyServices/">
<Company>
<CompanyId>10001</CompanyId>
<CoastalId>454564564564564564564564565465454546565555555</CoastalId>
<CompanyFederalId>345345</CompanyFederalId>
<CompanyName>Anytime Home</CompanyName>
<CompanyAddress>Address1</CompanyAddress>
<CompanyCity>TR</CompanyCity>
<CompanyState>UT</CompanyState>
<CompanyPostalCode>11</CompanyPostalCode>
<CompanyCountry>IT</CompanyCountry>
<CompanyTelephone>(999) 999-9999</CompanyTelephone>
</Company>
<CustomerId>33642</CustomerId>
</Customer>
</CompanyInvoice>
TSQL Code:
I have simply tried with this, but not getting any updates
Declare #DATAXML xml ='<?xml version="1.0"?>
<CompanyInvoice xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Customer xmlns="http://t.service/CompanyServices/">
<Company>
<CompanyId>10001</CompanyId>
<CoastalId>454564564564564564564564565465454546565555555</CoastalId>
<CompanyFederalId>345345</CompanyFederalId>
<CompanyName>Anytime Home</CompanyName>
<CompanyAddress>Address1</CompanyAddress>
<CompanyCity>TR</CompanyCity>
<CompanyState>UT</CompanyState>
<CompanyPostalCode>11</CompanyPostalCode>
<CompanyCountry>IT</CompanyCountry>
<CompanyTelephone>(999) 999-9999</CompanyTelephone>
</Company>
<CustomerId>33642</CustomerId>
</Customer>
</CompanyInvoice>'
;WITH XMLNAMESPACES('http://t.service/CompanyServices/' as x)
Select
a.value('x:CompanyId[1]','nvarchar(50)') as CompanyId,
a.value('x:CoastalId[1]','nvarchar(500)') as CoastalId,
a.value('x:CompanyName[1]','nvarchar(500)') as CompanyName
From #DATAXML.nodes('/CompanyInvoice/Customer/Company')as a (a)
Basically you have two options.
1.Introduce and use namespaces properly. Pay attention to namespaces scope.
2.Use a wildcard namespace (not recommended in production)
Declare #DATAXML xml = N'<?xml version="1.0"?>
<CompanyInvoice xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Customer xmlns="http://t.service/CompanyServices/">
<Company>
<CompanyId>10001</CompanyId>
<CoastalId>454564564564564564564564565465454546565555555</CoastalId>
<CompanyFederalId>345345</CompanyFederalId>
<CompanyName>Anytime Home</CompanyName>
<CompanyAddress>Address1</CompanyAddress>
<CompanyCity>TR</CompanyCity>
<CompanyState>UT</CompanyState>
<CompanyPostalCode>11</CompanyPostalCode>
<CompanyCountry>IT</CompanyCountry>
<CompanyTelephone>(999) 999-9999</CompanyTelephone>
</Company>
<CustomerId>33642</CustomerId>
</Customer>
</CompanyInvoice>';
WITH XMLNAMESPACES('http://t.service/CompanyServices/' as x)
Select
a.value('x:CompanyId[1]','nvarchar(50)') as CompanyId,
a.value('x:CoastalId[1]','nvarchar(500)') as CoastalId,
a.value('x:CompanyName[1]','nvarchar(500)') as CompanyName
From #DATAXML.nodes('CompanyInvoice/x:Customer/x:Company')as a (a);
--
select t.node.value('*:CompanyId[1]', 'int')
from #DATAXML.nodes('*:CompanyInvoice/*:Customer/*:Company') t(node);
Try This this just and other example
REferThis for more info
DECLARE #foo XML
SELECT #foo = N'
<harrys>
<harry>
<fish>0.015000000000</fish>
<bicycle>2008-10-31T00:00:00+01:00</bicycle>
<foo>ü</foo>
</harry>
<harry>
<fish>0.025000000000</fish>
<bicycle>2008-08-31T00:00:00+01:00</bicycle>
<foo>ä</foo>
</harry>
</harrys>
'
SELECT
CAST(CAST(y.item.query('data(fish)') AS varchar(30)) AS float),
CAST(LEFT(CAST(y.item.query('data(bicycle)') AS char(25)), 10) AS smalldatetime),
CAST(y.item.query('data(foo)') AS varchar(25))
FROM
#foo.nodes('/*') x(item)
CROSS APPLY
x.item.nodes('./*') AS y(item)
SELECT
CAST(CAST(x.item.query('data(fish)') AS varchar(30)) AS float),
CAST(LEFT(CAST(x.item.query('data(bicycle)') AS char(25)), 10) AS smalldatetime),
CAST(x.item.query('data(foo)') AS varchar(25))
FROM
#foo.nodes('harrys/harry') x(item)
SELECT
CAST(CAST(y.item.query('data(fish)') AS varchar(30)) AS float),
CAST(LEFT(CAST(y.item.query('data(bicycle)') AS char(25)), 10) AS smalldatetime),
CAST(y.item.query('data(foo)') AS varchar(25))
FROM
#foo.nodes('/harrys') x(item)
CROSS APPLY
x.item.nodes('./harry') AS y(item)
If this dosent work then
Alternate Link
I have a status table, and I must find the unused statuses.
The status code can be used in activity templates, which have an actionlist xml column with custom formulas.
So far I have written this, which works, but is incredibly slow (more than a minute to get 5000 lines), and I would need to speed it up a bit.
select *
from [status] s
where not exists (
select top 1 1
from Wf_ActivityTemplate at
where at.actionlist.value('.', 'nvarchar(max)') like '%#GetStatusId("' + s.code + '")%'
)
The actionlist column looks like this (irrelevant nodes removed).
As you can see, I need to search in the //ActionTriplet/Argument node, which is itself an xml node stored in text.
<ArrayOfActionTriplet xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ActionTriplet>
<Priority>1</Priority>
<Argument><?xml version="1.0" encoding="UTF-8" standalone="yes"?><string>#SetProp("STATUS",#GetStatusId("INTERNAL DESIGN REVIEW"));True</string></Argument>
<ActionCode>APPLY_FORMULA</ActionCode>
<TriggerTaskCode />
<TriggerTaskIsSecondary>false</TriggerTaskIsSecondary>
<TriggerTaskConditionFormula />
</ActionTriplet>
<ActionTriplet>
<Priority>2</Priority>
<Argument><?xml version="1.0" encoding="UTF-8" standalone="yes"?><string>#SetProp("STATUS",#GetStatusId("VALID"));True</string></Argument>
<ActionCode>APPLY_FORMULA</ActionCode>
<TriggerTaskCode />
<TriggerTaskIsSecondary>false</TriggerTaskIsSecondary>
<TriggerTaskConditionFormula />
</ActionTriplet>
</ArrayOfActionTriplet>
Your code is taking the whole content of your XML, converts it to nvarchar and performs a %x%-like-search, which is always slow. The bigger the text, the slower...
To show another approach I declare a table variable, which mocks up your table, with two rows (see the ID). I search for the code given in #YourCode:
DECLARE #YourCode NVARCHAR(100)=N'INTERNAL DESIGN REVIEW';
DECLARE #YourTable TABLE(ID INT IDENTITY, actionList XML);
INSERT INTO #YourTable(actionList) VALUES(
'<ArrayOfActionTriplet xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ActionTriplet>
<Priority>1</Priority>
<Argument><?xml version="1.0" encoding="UTF-8" standalone="yes"?><string>#SetProp("STATUS",#GetStatusId("INTERNAL DESIGN REVIEW"));True</string></Argument>
<ActionCode>APPLY_FORMULA</ActionCode>
<TriggerTaskCode />
<TriggerTaskIsSecondary>false</TriggerTaskIsSecondary>
<TriggerTaskConditionFormula />
</ActionTriplet>
<ActionTriplet>
<Priority>2</Priority>
<Argument><?xml version="1.0" encoding="UTF-8" standalone="yes"?><string>#SetProp("STATUS",#GetStatusId("VALID"));True</string></Argument>
<ActionCode>APPLY_FORMULA</ActionCode>
<TriggerTaskCode />
<TriggerTaskIsSecondary>false</TriggerTaskIsSecondary>
<TriggerTaskConditionFormula />
</ActionTriplet>
</ArrayOfActionTriplet>')
,(
'<ArrayOfActionTriplet xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ActionTriplet>
<Priority>1</Priority>
<Argument><?xml version="1.0" encoding="UTF-8" standalone="yes"?><string>#SetProp("STATUS",#GetStatusId("INTERNAL DESIGN REVIEW"));True</string></Argument>
<ActionCode>APPLY_FORMULA</ActionCode>
<TriggerTaskCode />
<TriggerTaskIsSecondary>false</TriggerTaskIsSecondary>
<TriggerTaskConditionFormula />
</ActionTriplet>
<ActionTriplet>
<Priority>2</Priority>
<Argument><?xml version="1.0" encoding="UTF-8" standalone="yes"?><string>#SetProp("STATUS",#GetStatusId("VALID"));True</string></Argument>
<ActionCode>APPLY_FORMULA</ActionCode>
<TriggerTaskCode />
<TriggerTaskIsSecondary>false</TriggerTaskIsSecondary>
<TriggerTaskConditionFormula />
</ActionTriplet>
</ArrayOfActionTriplet>');
--This is the query: You'll get back all Argument-elements, where the text within this element contains GetStatusId("TheCode").
SELECT s.ID
,arg.query('.')
FROM #YourTable AS s
CROSS APPLY actionList.nodes('/*:ArrayOfActionTriplet/*:ActionTriplet/*:Argument[fn:contains(.,fn:concat("GetStatusId("",sql:variable("#YourCode"),"")"))]') AS A(arg)
UPDATE
With this query you can cast the inner XML from the encoded form to real XML and get the full string readable:
SELECT s.ID
,arg.query('.')
,InnerXml.value('string[1]','nvarchar(max)')
FROM #YourTable AS s
CROSS APPLY actionList.nodes('/*:ArrayOfActionTriplet/*:ActionTriplet/*:Argument[fn:contains(.,fn:concat("GetStatusId("",sql:variable("#YourCode"),"")"))]') AS A(arg)
CROSS APPLY (SELECT CAST(arg.value('.','varchar(max)') AS XML)) AS Casted(InnerXml)
UPDATE 2: Faster, checking the existance only
If you want nothing more, than to check, wether there is an <Argument> containing your code or not, you might do this:
SELECT s.ID
FROM #YourTable AS s
WHERE actionList.exist('/*:ArrayOfActionTriplet/*:ActionTriplet/*:Argument[fn:contains(.,fn:concat("GetStatusId("",sql:variable("#YourCode"),"")"))]') =1
The final =1 means: The string is inlcuded. With =0 you'd get all rows, where this string is not included
This question already has answers here:
SQL Server FOR XML Enclosing Element?
(2 answers)
Closed 7 years ago.
I have a Script, which returns a XML using FOR XML in SQL 2008. Is there any way to add the version and encoding information in the beginning of the output. Eventually, i am planning to save the output in a file.
For example, right now my output looks like this
<Agents>
<Agent id="1">
<Name>Mike</Name>
<Location>Sanfrancisco</Location>
</Agent>
<Agent id="2">
<Name>John</Name>
<Location>NY</Location>
</Agent>
</Agents>
I would like to append the line <?xml version="1.0" encoding="UTF-8"?> in the beginning of the Xml output
So i want the output something like
<?xml version="1.0" encoding="UTF-8"?>
<Agents>
<Agent id="1">
<Name>Mike</Name>
<Location>Sanfrancisco</Location>
</Agent>
<Agent id="2">
<Name>John</Name>
<Location>NY</Location>
</Agent>
As #gbn points out in another answer and on another question, "the XML data is stored internally as ucs-2", and SQL Server doesn't include it when producing the data. However, you can convert the XML to a string and append the XML declaration at the beginning manually. However, simply using UTF-8 in the declaration would be inaccurate. The Unicode string which SQL produces is in UCS-2. For example, this will fail:
SELECT CONVERT(xml,N'<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(NVARCHAR(MAX),CONVERT(XML,N'<x>' + NCHAR(10176) + N'</x>')));
with error:
Msg 9402, Level 16, State 1, Line 1 XML parsing: line 1, character 38,
unable to switch the encoding
This, on the other hand, will work as expected:
SELECT CONVERT(xml,N'<?xml version="1.0" encoding="UCS-2"?>' + CONVERT(NVARCHAR(MAX),CONVERT(XML,N'<x>' + NCHAR(10176) + N'</x>')));
Here is code which will produce the full, declaration-laden XML string you seek for your example data:
DECLARE #Agents TABLE
(
AgentID int,
AgentName nvarchar(50),
AgentLocation nvarchar(100)
);
INSERT INTO #Agents (AgentID, AgentName, AgentLocation) VALUES (1, N'Mike', N'Sanfrancisco');
INSERT INTO #Agents (AgentID, AgentName, AgentLocation) VALUES (2, N'John', N'NY');
WITH BaseData AS
(
SELECT
(
SELECT
AgentID AS '#id',
AgentName AS 'Name',
AgentLocation AS 'Location'
FROM #Agents
FOR XML PATH('Agent'), ROOT('Agents'), TYPE
) AS AgentXML
), FullStringTable AS
(
SELECT
*,
'<?xml version="1.0" encoding="UCS-2"?>' +
CONVERT(nvarchar(max),AgentXML) AS FullString
FROM BaseData
)
SELECT
AgentXML AS OriginalXML,
FullString,
CONVERT(xml,FullString) AS FullStringConvertedToXML
FROM FullStringTable;
SQL Server internally always uses utf-16 ucs-2 so you could just append it like we did. That is, SQL Server would never generate anything with "utf-8".
Edit: after some digging:
http://www.devnewsgroups.net/group/microsoft.public.sqlserver.xml/topic60022.aspx
http://forums.asp.net/t/1455808.aspx
If you will not be attempting to manipulate the results as TSQL XML then the simplest thing to do will be create a varchar with the string you wish to append then add the XML to it using a CAST statemnt to convert the XML to varchar.
declare #testXML as XML
declare #testPrefix as varchar(255)
set #testPrefix = '<?xml version="1.0" encoding="UTF-8"?>'
set #testXML = '<Agents> <Agent id="1"> <Name>Mike</Name> <Location>Sanfrancisco</Location> </Agent> <Agent id="2"> <Name>John</Name> <Location>NY</Location> </Agent></Agents>'
select #testPrefix
Select #testXML
select #testPrefix + CAST(#testXML as varchar(max))