How to eliminate superfluous namespace declarations in SQL generated XML? - sql-server

I'm fine tuning a web app that calls SOAP services backed by SQL stored procedure calls. Typically the stored procs generate XML that becomes part of the SOAP response, and that XML has many superfluous xmlns namespace declarations. In pathological cases this can be 30% or more of the char encoded XML measured in bytes, e.g.:
<GetFooResponse xmlns="http://service.url/">
<GetFooResult>
<FooItems xmlns="http://www.thisisalongishurl.com/schema12345/version12345">
<Item xmlns="http://www.thisisalongishurl.com/schema12345/version12345" data="foo" />
<Item xmlns="http://www.thisisalongishurl.com/schema12345/version12345" data="foo" />
<Item xmlns="http://www.thisisalongishurl.com/schema12345/version12345" data="foo" />
<Item xmlns="http://www.thisisalongishurl.com/schema12345/version12345" data="foo" />
<Item xmlns="http://www.thisisalongishurl.com/schema12345/version12345" data="foo" />
</FooItems >
</GetFooResult>
</GetFooResponse>
The SQL I'm using to generate XML typically follows this pattern:
WITH XMLNAMESPACES(DEFAULT 'http://www.thisisalongishurl.com/schema12345/version12345')
SELECT
[Name],
[Value]
FROM
[Foo]
FOR XML PATH('Item'),
TYPE,
ROOT('FooItems');
Is there any way to avoid the generation of superfluous xmlns namespace declations on each Item XML element?
thanks.

WITH XMLNAMESPACES(
'http://www.thisisalongishurl.com/schema12345/version12345' AS short,
DEFAULT 'http://service.url/'
),
[Foo] ([short:Name], [short:Value]) AS
(
SELECT 'testName', 'testValue'
)
SELECT *
FROM [Foo]
FOR XML PATH('Item'),
TYPE,
ROOT('FooItems')
returns
<FooItems xmlns="http://service.url/" xmlns:short="http://www.thisisalongishurl.com/schema12345/version12345">
<Item>
<short:Name>testName</short:Name>
<short:Value>testValue</short:Value>
</Item>
</FooItems>

Related

Return XML cell data in XML query with SQL Server 2012

I have a table in SQL Server 2012 that contains some customer data. One of the columns in the table contains license data that is stored as XML. The type of the cell is nvarchar(MAX).
Is it possible to use FOR XML (or some other method) so that when the data is returned the XML from the license data is included as XML rather than a formatted string?
If I simply use FOR XML RAW, then the result is:
<Customers id="1" CustomerName="FirstCustomer"
LicenseData="<license customerid="1">...More data here...</license>" />
What I would liket to get is:
<Customers id="1" CustomerName="FirstCustomer">
<license customerid="1">
...More data here...
</license>
</Customers>
Is there any way to make that happen?
If the XML is a valid fragment then you can simply CAST it to XML.
SELECT CAST(MyColumn as XML) as MyXml
declare #temp table (id int, customername nvarchar(128), data nvarchar(max))
insert into #temp
select 1, 'FirstCustomer', '<license customerid="1"><element id="2">data1</element><element id="3"/></license>'
select id, customername, cast(data as xml)
from #temp
for xml raw
And you'll get results like this:
<row id="1" customername="FirstCustomer">
<license customerid="1">
<element id="2">data1</element>
<element id="3" />
</license>
</row>

Query XML value in sql

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

SQL - Read an XML node from a table field

I am using SQL Server 2008. I have a field called RequestParameters in one of my SQL table called Requests with XML data. An example would be:
<RequestParameters xmlns="http://schemas.datacontract.org/2004/07/My.Name.Space" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="1">
<Data z:Id="2" i:type="CheckoutRequest">
<UserGuid>7ec38c44-5aa6-49e6-9fc7-25e9028f2148</UserGuid>
<DefaultData i:nil="true" />
</Data>
</RequestParameters>
I ultimately want to retrieve the value of UserGuid. For that, I am doing this:
SELECT RequestParameters.value('(/RequestParameters/Data/UserGuid)[0]', 'uniqueidentifier') as UserGuid
FROM Requests
However, the results I am seeing are all NULL. What am I doing wrong?
You have to specify the default namespace and use [1] instead of [0].
WITH XMLNAMESPACES(default 'http://schemas.datacontract.org/2004/07/My.Name.Space')
SELECT RequestParameters.value('(/RequestParameters/Data/UserGuid)[1]', 'uniqueidentifier') as UserGuid
FROM Requests;
SQL Fiddle
declare #XML xml
set #XML = "<RequestParameters xmlns="http://schemas.datacontract.org/2004/07/My.Name.Space" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="1">
<Data z:Id="2" i:type="CheckoutRequest">
<UserGuid>7ec38c44-5aa6-49e6-9fc7-25e9028f2148</UserGuid>
<DefaultData i:nil="true" />
</Data>
</RequestParameters>"
select #XML.value('(/RequestParameters/Data /UserGuid)[1]', 'varchar')
'

Query ELMAH's XML field

The stock ELMAH_Error table uses an nText field to store an Error entry. I found that by adding a field of type XML; then adding this new field to the INSERT statement of the SPROC that populates the field; i could make better use of ELMAH's output.
Now I'd like to learn how to query specific element values within that XML field. The document is structured as:
<error [...]>
<serverVariables>
<item name="ALL_HTTP">
<value string="..." />
</item>
<item name="ALL_RAW">
<value string="..." />
</item>
.
.
.
</serverVariables>
</error>
I need to be able to query the value of specific items beneath .
So I'm looking at an example from the 15seconds.com article:
SELECT MyXml.value('(/root/product[#id="304"]/name)[1]', 'nvarchar(30)')
and am trying to maps those values to my field's structure - but can't. E.g.
select top 10 RealXML.value('(/error/serverVariables[#id="REMOTE_HOST"]/name)[0]', 'nvarchar(30)')
where REMOTE_HOST is formatted:
<item name="REMOTE_HOST">
<value string="55.55.55.55" />
</item>
much appreciated
This should work:
select top 10 RealXML.value('(/error/serverVariables/item[#name="REMOTE_HOST"]/value/#string)[1]', 'nvarchar(30)')
Tested using the following:
DECLARE #xml XML = '
<error>
<serverVariables>
<item name="ALL_HTTP">
<value string="..." />
</item>
<item name="ALL_RAW">
<value string="..." />
</item>
<item name="REMOTE_HOST">
<value string="55.55.55.55" />
</item>
</serverVariables>
</error>
'
SELECT #xml.value('(/error/serverVariables/item[#name="REMOTE_HOST"]/value/#string)[1]','nvarchar(30)')

XPath to fetch SQL XML value

Here is my problem: from the following XML that is within a column, I want to know if the value of a variable with the name 'Enabled' is equal to 'Yes' given a step Id and a component Id.
'<xml>
<box stepId="1">
<components>
<component id="2">
<variables>
<variable id="3" nom="Server" valeur="DEV1" />
<variable id="4" nom="Enabled" valeur="Yes" />
</variables>
</component>
<component id="3">
<variables>
<variable id="3" nom="Server" valeur="DEV1" />
<variable id="4" nom="Enabled" valeur="No" />
</variables>
</component>
</components>
</box>
<box stepId="2">
<components>
<component id="2">
<variables>
<variable id="3" nom="Server" valeur="DEV2" />
<variable id="4" nom="Enabled" valeur="Yes" />
</variables>
</component>
<component id="3">
<variables>
<variable id="3" nom="Server" valeur="DEV2" />
<variable id="4" nom="Enabled" valeur="No" />
</variables>
</component>
</components>
</box>
</xml>'
XQuery Against the xml Data Type
General XQuery Use Cases
XQueries Involving Hierarchy
XQueries Involving Order
Anything in Michael Rys blog
Update
My recomendation would be to shred the XML into relations and do searches and joins on the resulted relation, in a set oriented fashion, rather than the procedural fashion of searching specific nodes in the XML. Here is a simple XML query that shreds out the nodes and attributes of interest:
select x.value(N'../../../../#stepId', N'int') as StepID
, x.value(N'../../#id', N'int') as ComponentID
, x.value(N'#nom',N'nvarchar(100)') as Nom
, x.value(N'#valeur', N'nvarchar(100)') as Valeur
from #x.nodes(N'/xml/box/components/component/variables/variable') t(x)
However, if you must use an XPath that retrieves exactly the value of interest:
select x.value(N'#valeur', N'nvarchar(100)') as Valeur
from #x.nodes(N'/xml/box[#stepId=sql:variable("#stepID")]/
components/component[#id = sql:variable("#componentID")]/
variables/variable[#nom="Enabled"]') t(x)
If the stepID and component ID are columns, not variables, the you should use sql:column() instead of sql:variable in the XPath filters. See Binding Relational Data Inside XML Data.
And finaly if all you need is to check for existance you can use the exist() XML method:
select #x.exist(
N'/xml/box[#stepId=sql:variable("#stepID")]/
components/component[#id = sql:variable("#componentID")]/
variables/variable[#nom="Enabled" and #valeur="Yes"]')
I always go back to this article SQL Server 2005 XQuery and XML-DML - Part 1 to know how to use the XML features in SQL Server 2005.
For basic XPath know-how, I'd recommend the W3Schools tutorial.
I think the xpath query you want goes something like this:
/xml/box[#stepId="$stepId"]/components/component[#id="$componentId"]/variables/variable[#nom="Enabled" and #valeur="Yes"]
This should get you the variables that are named "Enabled" with a value of "Yes" for the specified $stepId and $componentId. This is assuming that your xml starts with an tag like you show, and not
If the SQL Server 2005 XPath stuff is pretty straightforward (I've never used it), then the above query should work. Otherwise, someone else may have to help you with that.

Resources