I am trying to add in a few elements in between the ROOT and PATH in a MSSQL 2008 Query. For Example I generate this something like this:
<Employees>
<Employee ID="1">
<LastName>David</LastName>
<FirstName>Larry</FirstName>
<Title>Writer</Title>
</Employee>
<Employee ID="2">
<LastName>Colbert</LastName>
<FirstName>Stephen</FirstName>
<Title>President of South Carolina</Title>
</Employee>
With
SELECT
[EmployeeID] AS '#ID',
[LastName], [FirstName],
[Title]
FROM
[dbo].[Employees]
FOR XML PATH('Employee'), ROOT('Employees')
I'd like to add a few elements like this:
<Employees>
<Company>ACME DYNAMITE</Company>
<CreateDate>JAN 01 2013</CreateDate>
<Employee ID="1">
<LastName>David</LastName>
<FirstName>Larry</FirstName>
<Title>Writer</Title>
</Employee>
<Employee ID="2">
<LastName>Colbert</LastName>
<FirstName>Stephen</FirstName>
<Title>President of South Carolina</Title>
</Employee>
I am using BCP to generate an output file so it would be possible to append a header and footer to the output file. If its possible in the query I'd like to do it that way. I have tried a variety of syntax but just can't seem to get it.
Any help is much appreciated.
SELECT
'ACME DYNAMITE' as Company,
'JAN 01 2013' as CreateDate,
(
SELECT
[EmployeeID] AS '#ID',
[LastName],
[FirstName],
[Title]
FROM
[dbo].[Employees]
FOR XML PATH('Employee'), TYPE
)
FOR XML PATH(''), ROOT('Employees')
Related
I have a SQL table receiving orders
<XMLGateway>
<Header>
....
</Header>
<Body>
<Orders>
<Order>
<ItemCode>315689</ItemCode>
<ProductName>Item1</ProductName>
</Order>
<Order>
<ItemCode>123456</ItemCode>
<ProductName>Product 1</ProductName>
</Order>
</Orders>
</Body>
I would then like to iterate through each order and insert them separately into my Orders table
insert into orders (ItemCode,ProductName) as separate records
Is there a simpler solution than a cursor?
You can try with this XQuery statement - no cursors needed for sure!
-- you have your XML in a SQL variable here
DECLARE #input XML = '<XMLGateway>
<Body>
<Orders>
<Order>
<ItemCode>315689</ItemCode>
<ProductName>Item1</ProductName>
</Order>
<Order>
<ItemCode>123456</ItemCode>
<ProductName>Product 1</ProductName>
</Order>
</Orders>
</Body>
</XMLGateway>'
-- Commented out the "INSERT" part, so you can try what the "SELECT" returns
-- INSERT INTO dbo.Orders (ItemCode, ProductName)
SELECT
-- pick out the "ItemCode" and "ProductName" subelements from the <Order> node
ItemCode = XC.value('(ItemCode/text())[1]', 'int'),
ProductName = XC.value('(ProductName/text())[1]', 'varchar(100)')
FROM
-- get the "<Order>" XML nodes, as a list of XML fragments
#input.nodes('/XMLGateway/Body/Orders/Order') AS XT(XC)
I have been given an XML document that I want to generate via a SQL script, I've not done something like this and haven't been able to find any examples that can lead me to being able to generate the final XML I need (and I'm not sure which of the possible methods available if one is better suited to what I need - EXPLICIT or PATH or if its even possible).
I'm hoping somebody with some experience in generating XML from SQL will be able to point me in the right direction (or tell me what I'm trying to do is impossible and that I need to do it with sub-queries).
The scenario is I'm returning product details from a single table (I would prefer to not have to do sub-queries for each of the values I need).
The xml I'm hoping to be able to generate looks like (I have no control over this format):
<records>
<record>
<fields>
<field name="id">
<values>
<value>666111</value>
</values>
</field>
<field name="name">
<values>
<value>
<![CDATA[My Product Title]]>
</value>
</values>
</field>
</fields>
</record>
<record>
...
</record>
</records>
The first method I've looked at is using FOR XML PATH
SELECT TOP 2
'id' AS "#name",
p.product_id as [value],
p.title
FROM products p
ORDER BY p.product_id DESC
FOR XML PATH ('field'), ROOT ('fields'), ELEMENTS;
and this gives me the XML:
<fields>
<field name="id">
<value>20624</value>
<title>test154</title>
</field>
<field name="id">
<value>20623</value>
<title>test153</title>
</field>
</fields>
Which gives me the '' that I need, but I can't then specify the layout I need for the next elements.
I also looked into FOR XML EXPLICIT
SELECT TOP 2
1 AS Tag, NULL AS Parent,
p.product_id AS [record!1!product_id!ELEMENT],
NULL AS [values!2!value!ELEMENT]
FROM products p
UNION ALL
SELECT TOP 2
2, 1,
p.product_id,
p.title
FROM products p
ORDER BY [record!1!product_id!ELEMENT] DESC
FOR XML EXPLICIT;
Which gave me the following XML:
<record>
<product_id>20624</product_id>
<values>
<value>test154</value>
</values>
</record>
<record>
<product_id>20623</product_id>
<values>
<value>test153</value>
</values>
</record>
I'm a bit lost in being able to build up the request or get something that is along the right lines (and I think I'm trying to do too much in a single lookup and that is the cause of my problem). Any help is appreciated - even if its pointing me at a good guide (the only ones I've found have been very poor when it comes to examples - they don't show the subtleties of how you can build/change them)
This is the query you might be looking for
The ,'' in the middle is a trick which allows you to create several elements with the same name one below the other...
DECLARE #tbl TABLE(id INT,name VARCHAR(100));
INSERT INTO #tbl VALUES
(666111,'My Product Title 111')
,(666222,'My Product Title 222');
SELECT
(
SELECT 'id' AS [field/#name]
,id AS [field/values/value]
,''
,'name' AS [field/#name]
,name AS [field/values/value]
FOR XML PATH('fields'),TYPE
)
FROM #tbl AS tbl
FOR XML PATH('record'),ROOT('records')
The result
<records>
<record>
<fields>
<field name="id">
<values>
<value>666111</value>
</values>
</field>
<field name="name">
<values>
<value>My Product Title 111</value>
</values>
</field>
</fields>
</record>
<record>
<fields>
<field name="id">
<values>
<value>666222</value>
</values>
</field>
<field name="name">
<values>
<value>My Product Title 222</value>
</values>
</field>
</fields>
</record>
</records>
UPDATE: As far as I know there is no clean way to add CDATA-sections
For some reasons people at Microsoft think, that CDATA sections are not necessary. Well, they aren't but still sometimes they are demanded...
The only clean way to add CDATA sections was to use FOR XML EXPLICIT. Another workaround was to put something like '|' + name + '#' (use two characters wich will never occur in your actual data.
Then you can cast the result to NVARCHAR(MAX), replace these characters on string base.
This would return your XML as string
SELECT
REPLACE(REPLACE(CAST(
(
SELECT
(
SELECT 'id' AS [field/#name]
,id AS [field/values/value]
,''
,'name' AS [field/#name]
,'|' + name + '#' AS [field/values/value]
FOR XML PATH('fields'),TYPE
)
FROM #tbl AS tbl
FOR XML PATH('record'),ROOT('records')
) AS NVARCHAR(MAX)),'|','<![CDATA['),'#',']]>')
At the moment you cast this back to XML the CDATA is gone :-(
something like that
declare #t table (id varchar(10))
insert into #t values ('1')
insert into #t values ('2')
select (
select
t.id 'fields/field/#id'
, t.id 'fields/field/name'
from #t t
for xml path(''), type
) 'records/record'
for xml path('')
The final SQL I used is:
SELECT TOP 2
(
SELECT
(SELECT 'id' AS [field/#id],
product_id [field/values/value]
FOR XML PATH(''), TYPE),
(SELECT 'title' AS [field/#id],
title [field/values/value]
FOR XML PATH(''), TYPE)
FOR XML PATH('fields'), TYPE
)
FROM products
FOR XML PATH('record'), ROOT('records')
As this allows me to manipulate the output a little easier.
Thank you to both #xdd and especially #Shnugo for your answers! The end solution is based on #Shnugo's suggestion, just with avoiding the trick of putting extra blank rows in.
I have following T-SQL code:
DECLARE #DesiredCategories TABLE(CategoryID INT, Search BIT, SearchText NVARCHAR(50));
INSERT INTO #DesiredCategories
SELECT X.Col.query('id').value('.', 'INT') as CategoryID,
X.Col.value('#search', 'BIT') as Search,
X.Col.query('text').value('.', 'NVARCHAR(MAX)') as SearchText
FROM .nodes('root/node') X(Col);
#XMLCategoryIDs contains XML like this:
<root>
<node search="0"><id>10088</id><text></text></node>
<node search="0"><id>10087</id><text></text></node>
<node search="0"><id>10090</id><text></text></node>
</root>
Sadly, this code have very high subtree cost (for following code snippet it's ~900) and seems to be a perfomance bottleneck.
Execution plan is included - http://pastebin.com/ptnqJ4jX
We are using XML to send varying number of parameters (1-5) to stored procedure.
And yes, I'm total beginner with XML manipulation in SQL.
If your xml is stored in a table, you can create a primary xml index to reduce subtree cost for queries against that xml at a later time. You're basically shifting the workload to earlier in the process, but this might help your situation.
IF OBJECT_ID('tempdb..#Table') IS NOT NULL
DROP TABLE #Table;
CREATE TABLE #Table
(
ID INT IDENTITY PRIMARY KEY
,DocumentId INT
,Xml XML
)
CREATE PRIMARY XML INDEX PXML_Table_Xml
ON #Table (Xml);
GO
INSERT INTO #Table
(
DocumentId
,Xml
)
VALUES
(
1
,
'
<root>
<node search="0"><id>10088</id><text></text></node>
<node search="0"><id>10087</id><text></text></node>
<node search="0"><id>10090</id><text></text></node>
</root>
'
)
DECLARE #DesiredCategories TABLE(DocumentId INT,CategoryID INT, Search BIT, SearchText NVARCHAR(50));
INSERT INTO #DesiredCategories
SELECT
DocumentId
,CategoryID
,Search
,SearchText
FROM #Table
CROSS APPLY
(
SELECT
X.Col.query('id').value('.', 'INT') as CategoryID,
X.Col.value('#search', 'BIT') as Search,
X.Col.query('text').value('.', 'NVARCHAR(MAX)') as SearchText
FROM Xml.nodes('root/node') X(Col)
) A
SELECT * FROM #DesiredCategories
I'm trying to generate an XML output from SQL and need to use a UNION statement and also name the output column.
I had this working before when I didn't need to use a UNION statement using:
select(
SELECT
[CompanyName],
[Address1],
[Address2],
[Address3],
[Town],
[County],
[Postcode],
[Tel],
[Fax],
[Email],
[LocMap]
FROM [UserAccs] FOR XML PATH ('AccountDetails'), root ('Root')
) as XmlOutput
Which named the output XML column as XmlOutput
I am now trying:
select(
SELECT
[CompanyName],
[Address1],
[Address2],
[Address3],
[Town],
[County],
[Postcode],
[Tel],
[Fax],
[Email],
[LocMap]
FROM [UserAccs]
UNION
SELECT
[CompanyName],
[Address1],
[Address2],
[Address3],
[Town],
[County],
[Postcode],
[Tel],
[Fax],
[Email],
[LocMap]
FROM [UserAppAccs]
FOR XML PATH ('AccountDetails'), root ('Root')
) as XmlOutput
But receive an error message, does anyone know a way around this?
The FOR XML clause is invalid in views, inline functions, derived tables, and subqueries when they contain a set operator. To work around, wrap the SELECT containing a set operator using derived table syntax and apply FOR XML on top of it.
Thanks
J.
Wrap your 2 selects on a single one like so:
select (
select id, name from (
select id, name
from xmltest
UNION
select id, name
from xmltest
) A
FOR XML PATH ('AccountDetails'), root ('Root')
) As XmlOutput
I have some XML data stored in a varchar(max) column on SQL Server 2005. The data is in the form (FQTN = fully qualified type name):
<?xml version="1.0" encoding="utf-16"?>
<History xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EntityViews>
<EntityProxy Type="FQTN" Key="386876" />
<EntityProxy Type="FQTN" Key="387981" />
<!-- etc. -->
</EntityViews>
</History>
How can I select Type, Key so that I get a tabular result from the XML data in this column for a single row? The table has an identity primary key named HistoryId.
;with cteCastToXML as (
select CAST(YourColumn as xml) as x
from YourTable
)
select h.ep.value('#Type','varchar(10)') as [Type],
h.ep.value('#Key', 'varchar(10)') as [Key]
from cteCastToXML
cross apply x.nodes('/History/EntityViews/EntityProxy') as h(ep)
My recommendation would be two fold.
If this is what you will be doing with the column, change the column to be an XML column.
If you need to do this one time, look at taking the value and converting it to XML, then you can operate on it like you would normally. (Here is a link on how to convert).