I have nearly accomplished what I need, but am missing one last thing. I have 2 tables being joined to create an XML output. I need elements of both tables to be used in the same SELECT statement, but am having trouble making that work. This code is what I have:
SELECT
1 as Tag,
0 as Parent,
RTRIM(dbo.DataItemInfo.DataItem) as [DataItem!1!name]
--RTRIM(dbo.DataItemInfo.DataItem) as [dbo.DataSchedule.DataItemValue!1!]
FROM
dbo.DataItemInfo
INNER JOIN
dbo.DataSchedule
ON dbo.DataSchedule.SignID = dbo.DataItemInfo.SignID
AND dbo.DataSchedule.SignID=#ParamSignID
AND dbo.DataSchedule.ScheduleID = dbo.DataItemInfo.ScheduleID
FOR XML EXPLICIT, ROOT('DataItems')
Where the commented out section is one of the things I tried. At the moment, it produces the output:
<DataItems>
<DataItem name="Test1" />
<DataItem name="Test2" />
<DataItem name="Test3" />
<DataItem name="Test4" />
<DataItem name="Test5" />
</DataItems>
But I want:
<DataItems>
<DataItem name="Test1">ValFromScheduleTableHere<DataItem/>
<DataItem name="Test2">ValFromScheduleTableHere<DataItem/>
<DataItem name="Test3">ValFromScheduleTableHere<DataItem/>
<DataItem name="Test4">ValFromScheduleTableHere<DataItem/>
<DataItem name="Test5">ValFromScheduleTableHere<DataItem/>
</DataItems>
I know how to populate the "ValFromScheduleTableHere" from the original table, but not from a second table. Thanks for the help.
FOR XML PATH syntax is easier and you can get the desired result with this SQL.
SELECT
(
SELECT
RTRIM(dbo.DataItemInfo.DataItem) as 'DataItem/#name'
DataSchedule.DataitemValue as 'DataItem'
FROM
dbo.DataItemInfo
INNER JOIN
dbo.DataSchedule
ON dbo.DataSchedule.SignID = dbo.DataItemInfo.SignID
AND dbo.DataSchedule.SignID=#ParamSignID
AND dbo.DataSchedule.ScheduleID = dbo.DataItemInfo.ScheduleID
FOR XML PATH(''), TYPE
)
FOR XML PATH(''), ROOT('DataItems')
I realize this has been answered and even that EXPLICIT mode is no longer being used, but I happened to figure it out so I thought I would share. For better or worse, you were very close. The column name for the field to be the entity value was missing the "directive" portion, which is the 4th (and optional) part of the funky column name !-convention. You needed !xml as the last part, and of course to have adjusted what is in the RTRIM and the rest of the alias, which would have looked like:
RTRIM(DataSchedule.DataItemValue) as [DataItem!1!!xml]
Anyone can test the theory with the following (I aligned the column aliases to more easily see the difference in the 3rd alias):
SELECT 1 AS [Tag],
0 AS [Parent],
RTRIM(so.[type]) AS [Object!1!Type],
so.create_date AS [Object!1!CreateDate],
so.[name] AS [Object!1!!xml]
FROM sys.objects so
FOR XML EXPLICIT, ROOT('Objects');
Output:
<Objects>
<Object Type="S" CreateTate="2014-02-20T20:48:34.573">sysrscols</Object>
<Object Type="S" CreateTate="2009-04-13T12:59:05.513">sysrowsets</Object>
<Object Type="S" CreateTate="2014-02-20T20:48:34.807">sysclones</Object>
<Object Type="S" CreateTate="2009-04-13T12:59:05.500">sysallocunits</Object>
<Object Type="S" CreateTate="2003-04-08T09:13:37.267">sysfiles1</Object>
...
</Objects>
For more info, please see the following MSDN page: Use EXPLICIT Mode with FOR XML
Related
I have to build this payload
<?xml version="1.0" encoding="utf-8"?>
<shipment>
<software>
<application>MYRTL</application>
<version>1.0</version>
</software>
<security>
<customer>X00000</customer>
<user>X00000</user>
<password>password1</password>
<langid>IT</langid>
</security>
<consignment action="I" cashondeliver="N" international="N" insurance="N">
<labelType>T</labelType>
<senderAccId>200200</senderAccId>
<consignmenttype>T</consignmenttype>
<actualweight>00008000</actualweight>
<actualvolume>0000018</actualvolume>
<totalpackages>2</totalpackages>
<packagetype>C</packagetype>
<division>D</division>
<product>N</product>
<insurancevalue>0000000000000</insurancevalue>
<insurancecurrency>EUR</insurancecurrency>
<reference><![CDATA[22X000223]]></reference>
<collectiondate>20220818</collectiondate>
<termsofpayment>S</termsofpayment>
<systemcode>RL</systemcode>
<systemversion>1.0</systemversion>
<codfvalue>0000000000000</codfvalue>
<codfcurrency>EUR</codfcurrency>
<goodsdesc><![CDATA[Bread, Butter & Puré]]></goodsdesc>
<addresses>
<address>
<addressType>S</addressType>
<vatno>123456789123</vatno>
<addrline1><![CDATA[Via Mondovì, n° 23]]></addrline1>
<postcode><![CDATA[20125]]></postcode>
<phone1><![CDATA[345]]></phone1>
<phone2><![CDATA[3456345]]></phone2>
<name><![CDATA[Jack & Joe srl]]></name>
<country><![CDATA[IT]]></country>
<town><![CDATA[Arquà Polesine]]></town>
<province><![CDATA[RO]]></province>
<email><![CDATA[mail#jack_and_joe.it]]></email>
</address>
<address>
<addressType>C</addressType>
<addrline1><![CDATA[12° Reggimento Granatieri, 14]]></addrline1>
<postcode><![CDATA[00195]]></postcode>
<phone1><![CDATA[321]]></phone1>
<phone2><![CDATA[3214321]]></phone2>
<name><![CDATA[Giosuè Rossë]]></name>
<country><![CDATA[IT]]></country>
<town><![CDATA[Gambolo']]></town>
<province><![CDATA[TV]]></province>
<email><![CDATA[mario#rossi.it]]></email>
</address>
<address>
<addressType>R</addressType>
<addrline1><![CDATA[Hauptstraße 13]]></addrline1>
<postcode><![CDATA[34100]]></postcode>
<phone1><![CDATA[333]]></phone1>
<phone2><![CDATA[333444555]]></phone2>
<name><![CDATA[Noè Giassù]]></name>
<country><![CDATA[IT]]></country>
<town><![CDATA[Völs am Schlern]]></town>
<province><![CDATA[BZ]]></province>
<email><![CDATA[mail#noe.it]]></email>
</address>
</addresses>
<collectiontrg>
<priopntime>0900</priopntime>
<priclotime>1200</priclotime>
<secopntime>1400</secopntime>
<secclotime>1800</secclotime>
<availabilitytime>1600</availabilitytime>
<pickupdate>18.08.2022</pickupdate>
<pickuptime>1600</pickuptime>
<pickupdays>1</pickupdays>
<pickupinstr><![CDATA[Test Shipment ===> DO NOT COLLECT <===]]></pickupinstr>
</collectiontrg>
<dimensions itemaction="I">
<itemsequenceno>1</itemsequenceno>
<itemtype>C</itemtype>
<itemreference><![CDATA[22X0002223_1]]></itemreference>
<volume>0000009</volume>
<weight>00003000</weight>
<length>030000</length>
<heigh>010000</heigh>
<width>030000</width>
<quantity>1</quantity>
</dimensions>
<dimensions itemaction="I">
<itemsequenceno>2</itemsequenceno>
<itemtype>C</itemtype>
<itemreference><![CDATA[22X0002223_2]]></itemreference>
<volume>0000009</volume>
<weight>00005000</weight>
<length>030000</length>
<heigh>010000</heigh>
<width>030000</width>
<quantity>1</quantity>
</dimensions>
</consignment>
</shipment>
I had the bad idea to use T-SQL since all data are in SQL Server DB
I thought it was quite easy, and actually, it was, since was just required to nest some FOR XML PATH, TYPE subqueries.
Problems arose when considered that some fields could contain not standard charachters, therefore was better to use some CDATA fields.
I faced several problems since it appears that the only way to preserve CDATA is using FOR XML EXPLICIT that seems to be deprecated.
However it was very difficult to find documentation.
Fortunately I found this post that helped me to make the reverse path:
Therefore I built a sproc with XML Explicit format:
SELECT 1 AS Tag,
NULL AS Parent,
'MYRTL' AS 'software!1!application!element',
'1.0' AS 'software!1!version!element',
NULL AS 'security!2!customer!element',
...
NULL AS 'security!2!langid!element',
NULL AS 'consignment!3!action',
...
NULL AS 'consignment!3!goodsdesc!CDATA',
NULL AS 'addresses!4!address',
NULL AS 'address!5!addressType!element',
...
NULL AS 'address!5!town!CDATA',
...
NULL AS 'collectiontrg!9!priopntime!element',
...
NULL AS 'collectiontrg!9!pickupdate!element',
UNION ALL
SELECT 2 AS Tag,
NULL AS Parent,
...
UNION ALL
SELECT 3 AS Tag,
NULL AS Parent,
...
UNION ALL
SELECT 9 AS Tag,
3 AS Parent,
...
FOR XML EXPLICIT, ROOT('shipment')
It seems to be working well... although I think there has to be a better way to build it.
Now I have a further issue that I do not know how to solve, or better, I could solve it using a dynamic query, but I would avoid it:
New issue is that node shipment.consignment.addresses.address where addressType=='C'
has to be omitted if it contains the same values as shipment.consignment.addresses.address where addressType=='S'
furthermore the node shipment.consignment.collectiontrg has to appear only if the variable pickupDate is not null
Is there a way to avoid the dynamic query?
Is there a better way to build this query?
Thanks
I stored my data in XML format in SQL server, in this way
**<Column Name="GROSS" DataType="float" Value="939760" />**
but somehow one column name (GROSS) in my XML data is stored twice, now I want to remove/rename one of them.
Below are screenshots of my database view:
Table view
XML view
This it what I tried, but it only changed the value, it did not rename the column name.
update Aquara7bc772839.EmpTransaction set TransactionFieldDetails.modify('replace value of (/PayDetails/Column[#Name="LEV_ENCASHRATE"]/#Value)[1] with "796.00"') WHERE Id = 276620;
I have highlighted my column names in above image link please check
I want to remove or rename one column.
You can use the XML.modify() method with a delete node instruction...
declare #EmpTransaction table (
Id int not null,
TransactionFieldDetails xml
);
insert #EmpTransaction values (
276620,
N'<PayDetails>
<Column Name="GROSS" DataType="float" Value="939760" />
<Column Name="GROSS" DataType="float" Value="939760" />
</PayDetails>'
);
update #EmpTransaction
set TransactionFieldDetails.modify('delete /PayDetails/Column[#Name="GROSS"][2]')
where Id = 276620;
select * from #EmpTransaction;
Which gives you...
<PayDetails>
<Column Name="GROSS" DataType="float" Value="939760" />
</PayDetails>
Note that the node index is 1-based, i.e.: Column[#Name="GROSS"][1] would remove the first GROSS node, Column[#Name="GROSS"][2] removes the second GROSS node.
As to how you got two GROSS values in the first place ... whatever created the XML for you probably lists the GROSS column twice.
For your next question: Please do not poste pictures. Best was, to provide a MCVE (a stand-alone sample to reproduce your issue).
You can try something along this:
This is such a stand-alone sample
DECLARE #mockupTable TABLE(ID INT IDENTITY, YourXml XML);
INSERT INTO #mockupTable VALUES
(N'<PayDetails>
<Column Name="blah" DataType="string" Value="Some blah" />
<Column Name="GROSS" DataType="float" Value="1.1" />
<Column Name="Another" DataType="string" Value="One more" />
<Column Name="GROSS" DataType="float" Value="1.2" />
</PayDetails>')
,(N'<PayDetails>
<Column Name="blah" DataType="string" Value="Some blah" />
<Column Name="GROSS" DataType="float" Value="2.0" />
<Column Name="Another" DataType="string" Value="One more" />
<Column Name="GROSS" DataType="float" Value="2.0" />
</PayDetails>');
--This query will first check, if there are different values for GROSS within one XML. This might be a reason to look a bit closer before deleting them.
SELECT t.ID
,t.YourXml
FROM #mockupTable t
WHERE t.YourXml.value('count(distinct-values(/PayDetails/Column[#Name="GROSS"]/#Value))','int')>1;
--And this statement will return each <Column> just once per name (always the first of its kind)
UPDATE #mockupTable SET YourXml=YourXml.query(N'
<PayDetails>
{
for $elmt in distinct-values(/PayDetails/Column/#Name)
return /PayDetails/Column[#Name=$elmt][1]
}
</PayDetails>
');
--Check the output
SELECT * FROM #mockupTable
The idea in short:
With the Xpath /PayDetails/Column[#Name="GROSS"] we are reducing the observed set to columns, where the attribute Name equals GROSS. distinct-values() is a XQuery-function returning each value in a list just once. So we can use count() to check, if there are differing values for GROSS within one XML.
The UPDATE uses XQuery's FLWOR abilities. We use again distinct-values to get all values for Name within <Column>. Then we return just the first (see the [1]) for each name.
UPDATE: Check for doubled elements
With this query you can run through your whole table to search for any non-unique column name per XML:
SELECT t.YourXml.query(N'
for $elmt in distinct-values(/PayDetails/Column/#Name)
return <NameCount Name="{$elmt}" Count="{count(/PayDetails/Column[#Name=$elmt])}"/>
').query('/NameCount[#Count>1]')
FROM #mockupTable t;
I've been working on a query to pull XML data from a SQL Server database. I want to extract a specific row from the XML. If I hard code the ID in the .nodes line, it works as intended, but I can't get past a syntax error when replacing the hard coded value with sql:variable and have exhausted my Google-foo
'''SQL
DECLARE #XML XML = '<DynamicModel>
<AvailableElements>
<Control Id="97a0d1c6-f2b4-4f6f-8d01-f6110f1679af">
<Property Name="Name" Value="Picklist1" />
<Property Name="Id" Value="97a0d1c6-f2b4-4f6f-8d01-f6110f1679af" />
<Property Name="Label" Value="Label value here" />
<Property Name="SelectedItemId" Value="Value in here" Type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" IsRequired="True" IsReference="True" />
<Elements />
<AvailableElements />
</Control>
</AvailableElements>
</DynamicModel>'
DECLARE #ElementID NVARCHAR(100) = '97a0d1c6-f2b4-4f6f-8d01-f6110f1679af'
/* WORKS */
SELECT
codedValue.value('#Name[1]', 'nvarchar(500)') AS ItemName,
codedValue.value('#Value[1]', 'nvarchar(500)') AS ItemValue
FROM #XML.nodes('/DynamicModel/AvailableElements/Control[#Id="97a0d1c6-f2b4-4f6f-8d01-f6110f1679af"]/Property') AS x(codedValue)
WHERE codedValue.value('#Name[1]', 'nvarchar(500)') IN ('Text','Date','SelectedItemId','Label')
/* DOES NOT WORK */
SELECT
codedValue.value('#Name[1]', 'nvarchar(500)') AS ItemName,
codedValue.value('#Value[1]', 'nvarchar(500)') AS ItemValue
FROM #XML.nodes('/DynamicModel/AvailableElements/Control[#Id="sql:variable("#ElementID")]/Property') AS x(codedValue)
WHERE codedValue.value('#Name[1]', 'nvarchar(500)') IN ('Text','Date','SelectedItemId','Label')
'''
The hard coded version returned two rows which is correct based on the XML. The version using sql:variable errored with the message 'XQuery [nodes()]: Syntax error near '#', expected ']'.'
Your Query should be like this-
SELECT
codedValue.value('#Name[1]', 'nvarchar(500)') AS ItemName,
codedValue.value('#Value[1]', 'nvarchar(500)') AS ItemValue
FROM #XML.nodes('/DynamicModel/AvailableElements/Control[#Id=sql:variable("#ElementID")]/Property') AS x(codedValue)
WHERE codedValue.value('#Name[1]', 'nvarchar(500)') IN ('Text','Date','SelectedItemId','Label')
I can't figure out what I'm doing wrong. I have XML stored in a table column as text. I'm taking the ID, and the XML text and querying it into a temp table that stores the XML as XML type.
Each Order in the XML has multiple licenses in it that I need to pull out and create a new table with OrderID and License ID. But I can't tell what I'm doing wrong.
So, I'm trying to start basic but I can't seem to even just get the Account Info from the first Node.
The XML looks like this:
<ns1:OrderFromCRM xmlns:ns1="http://company.com/licensing/neworder/v2">
<ns1:AccountInfo Name="Company Name" AccountId="A012345" />
<ns1:OrderInfo CRMOrderId="S147360" Date="2/23/2017 12:00:00 AM" ffEmail="emailaddress.#gmail.com" >
<ns1:Licensing>
<ns1:Foundations>
<ns1:Foundation LicenseId="L012345678" Action="Create" Environment="Production" Type="Enterprise">
<Metadata>
<AllowedInstances>999</AllowedInstances>
</Metadata>
</ns1:Foundation>
<ns1:Foundation LicenseId="L012345698" Action="Create" Environment="Production" Type="Enterprise">
<Metadata>
<AllowedInstances>999</AllowedInstances>
</Metadata>
</ns1:Foundation>
</ns1:Foundations>
<ns1:Licenses Type="Create">
<ns1:License LicenseId="L0123451234" ProductFamily="Fam1" Product="EStudio" LicenseType="Perpetual" StartDate="2017-02-23" ExpiryDate="2017-12-18" MaintenanceExpiryDate="2017-12-18">
<ns1:Capabilities>
<ns1:Capability Name="T1" />
<ns1:Capability Name="Q1" />
<ns1:Capability Name="B1" />
</ns1:Capabilities>
</ns1:License>
<ns1:License LicenseId="L333356675" ProductFamily="Fam1" Product="EStudio" LicenseType="Perpetual" StartDate="2017-02-23" ExpiryDate="2017-12-18" MaintenanceExpiryDate="2017-12-18">
<ns1:Capabilities>
<ns1:Capability Name="T1" />
<ns1:Capability Name="Q1" />
<ns1:Capability Name="B1" />
</ns1:Capabilities>
</ns1:License>
The SQL I wrote is:
CREATE TABLE #demoData
(
ActivationCode NVARCHAR(100) NOT NULL,
OrderXMLText XML
)
SELECT OrderId, OrderXMLText.value('(/OrderFromCRM/AccountInfo)[1]', 'varchar(30)')
FROM #DEMODATA
I mentioned I need the OrderID and the LicenseId but even with this, I can't get anything. Am I on right track? First, what am I missing? Second, once this is formatted correctly, how do I get the nested LicenseIds in the XML?
Thanks so much for any help. I've been trying to make this work for a couple days
You’re missing the namespace so the query isn’t matching the xml, therefore it doesn’t find the elements you’re querying.
Add
;WITH XMLNAMESPACES
https://learn.microsoft.com/en-us/sql/t-sql/xml/with-xmlnamespaces
Assuming you have a complete, valid XML document in your table (the one you're showing is lacking several closing tags), then you could try something like this:
;WITH XMLNAMESPACES('http://company.com/licensing/neworder/v2' AS ns1)
SELECT
ActivationCode,
CRMOrderId = XC.value('#CRMOrderId', 'varchar(100)'),
FoundationsLicenseId = xcf.value('#LicenseId', 'varchar(50)'),
LicensesLicenseId = xcl.value('#LicenseId', 'varchar(50)')
FROM
#demoData
CROSS APPLY
OrderXMLText.nodes('/ns1:OrderFromCRM/ns1:OrderInfo') AS XT(XC)
CROSS APPLY
XC.nodes('ns1:Licensing/ns1:Foundations/ns1:Foundation') AS XTF(XCF)
CROSS APPLY
XC.nodes('ns1:Licensing/ns1:Licenses/ns1:License') AS XTL(XCL)
First of all, you need to include and respect the XML namespace in your XQuery - that's what I do with the WITH XMLNAMESPACES() directive.
Next, you need to use .nodes() to get a list of XML fragments for each <OrderInfo> node, which is located below the root node (<OrderFromCRM>). This is the first CROSS APPLY. This returns a list of XML fragments, one for each <OrderInfo> node.
Then you need to reach into these XML fragments, and again use CROSS APPLY with the .nodes() function to get a list of the <Foundation> elements (contained in the <Licensing>/<Foundations> subtree to get the license Id's from those nodes. In addition, you need a second CROSS APPLY to get all the <License> subnodes under <Licensing>/<Licenses> to get those LicenseId attributes.
That should return an output something like:
Hope this helps you some!
I need to get some information from XML in SQL Server 2008, but I cannot even get basic attribute from it. All samples that I tried failed. Table name is Item, xml column name is Data.
Simplified xml looks like this:
<AnchoredXml xmlns="urn:schema:Microsoft.Rtc.Management.ScopeFramework.2008" SchemaWriteVersion="2">
<Key ScopeClass="Global">
<SchemaId Namespace="urn:schema:Microsoft.Rtc.Management.Deploy.Topology.2008" ElementName="Topology" />
<AuthorityId Class="Host" InstanceId="00000000-0000-0000-0000-000000000000" />
</Key>
<Dictionary Count="1">
<Item>
<Key />
<Value Signature="a3502dd0-8c16-4023-9eea-30ea1c7a3a2b">
<Topology xmlns="urn:schema:Microsoft.Rtc.Management.Deploy.Topology.2008">
<Services>
<Service RoleVersion="1" ServiceVersion="6" Type="Microsoft.Rtc.Management.Deploy.Internal.ServiceRoles.FileStoreService">
<ServiceId SiteId="1" RoleName="FileStore" Instance="1" />
<DependsOn />
<InstalledOn>
<ClusterId SiteId="1" Number="1" />
</InstalledOn>
<Ports xmlns="urn:schema:Microsoft.Rtc.Management.Deploy.ServiceRoles.2008" />
<FileStoreService xmlns="urn:schema:Microsoft.Rtc.Management.Deploy.ServiceRoles.2008" ShareName="lyncShare" />
</Service>
</Services>
</Topology>
</Value>
</Item>
</Dictionary>
</AnchoredXml>
I need to read information in AnchoredXml/Key/SchemaId/#NameSpace to select the right xml (there are more rows). Sample xml above is the right one. And after that I need to find the right service with
Type="Microsoft.Rtc.Management.Deploy.Internal.ServiceRoles.FileStoreService"
where is FileStoreService/#ShareName that I need.
I've tried to print the Namespace attributte for the start, but no sample code is working.
A few tries:
SELECT c.p.value('(#Namespace)[1]', 'varchar(50)') as 'Nmspace'
FROM Item
CROSS APPLY Data.nodes('/AnchoredXml/Key/SchemaId') c(p)
returns empty result set
SELECT Data.value('(/AnchoredXml/Key/SchemaId/#Namespace)[1]', 'varchar(50)')
FROM Item
returns NULL for all rows
SELECT
It.Data.exist('/AnchoredXml/Key/SchemaId[#Namespace="Microsoft.Rtc.Management.Deploy.Topology.2008"]')
FROM [xds].[dbo].[Item] AS It
returns 0's for all rows also without quotes ("")
A working sample code to get at least attribute test would be maybe sufficient and I would figure out the rest.
Could you please help me find errors in my queries or maybe identify some other problem?
Thanks
You're ignoring all the XML namespaces in your XML document! You need to pay attention to those and respect them!
There are XML namespaces on:
the root node <AnchoredXml>
(XML namespace: urn:schema:Microsoft.Rtc.Management.ScopeFramework.2008)
the subnode <Topology>
(XML ns: urn:schema:Microsoft.Rtc.Management.Deploy.Topology.2008)
the subnode <FileStoreService>
(XML ns: urn:schema:Microsoft.Rtc.Management.Deploy.ServiceRoles.2008)
Try this:
-- respect the XML namespaces!!
;WITH XMLNAMESPACES(DEFAULT 'urn:schema:Microsoft.Rtc.Management.ScopeFramework.2008',
'urn:schema:Microsoft.Rtc.Management.Deploy.Topology.2008' AS t,
'urn:schema:Microsoft.Rtc.Management.Deploy.ServiceRoles.2008' AS fss)
SELECT
ShareName = Data.value('(/AnchoredXml/Dictionary/Item/Value/t:Topology/t:Services/t:Service/fss:FileStoreService/#ShareName)[1]', 'varchar(50)')
FROM
dbo.Item
In my case, this returns:
ShareName
-----------
lyncShare