I have a query that returns data organized by group. I am wanting to have them come out in a grouped XML similar to the format below. I am planning to pass it in as a XML message into BizTalk using the WCF-SQL port adapter.
Data:
ID GroupID Item ID FileName
1 1 1 File001.txt
2 1 2 File001.txt
3 2 3 File002.txt
4 2 4 File002.txt
5 2 5 File002.txt
6 3 6 File003.txt
7 3 7 File003.txt
8 null 8 File004.txt
9 null 9 File005.txt
XML Ouput (input to BizTalk)
<GroupInfo ID=1 FileName=File001.txt>
<Items>
<Item ID=1 />
<Item ID=2 />
</Items>
</GroupInfo>
<GroupInfo ID=2 FileName=File002.txt>
<Items>
<Item ID=3 />
<Item ID=4 />
<Item ID=5 />
</Items>
</GroupInfo>
<GroupInfo ID=3 FileName=File003.txt>
<Items>
<Item ID=6 />
<Item ID=7 />
</Items>
</GroupInfo>
<GroupInfo FileName=File004.txt>
<Items>
<Item ID=8 />
</Items>
</GroupInfo>
<GroupInfo FileName=File005.txt>
<Items>
<Item ID=9 />
</Items>
</GroupInfo>
I'm not sure what to do to get the output in the required format. Please help.
To accomplish this, you would have to use FOR XML results from the Stored Procedure.
All documented here: http://technet.microsoft.com/en-us/library/ms178107.aspx
In this case, you might find it easier to join the table to itself on GroupID as FOR XML AUTO will automatically create child records for the joined rows.
Related
I have been looking for solution for selecting parent-child relation from table [Group] which contains a xml column.
[Group] table has the following structure:
ID - int
Content - xml
There is xml data - parent-child relation in column Content
<root>
<person name="John">
<device name="notebook" />
<device name="xbox" />
</person>
<person name="Jane">
<device name="TV" />
</person>
<person name="Mark">
</person>
</root>
I would like to select data in the following format:
Group Id
PersonName
DeviceName
1
John
notebook
1
John
xbox
1
Jane
TV
Because Mark has no device assigned, there is no row for Mark in result.
Is it possible to achieve this result in a SELECT query?
As I mentioned, you can use XQuery for this. As you don't want any rows for Mark, I go straight to the device node, in the nodes method, as this means that no rows for Mark will be found. Then you can go back up one level to get the person's name:
SELECT V.ID AS GroupID,
p.d.value('../#name','nvarchar(50)') AS PersonName,
p.d.value('#name','nvarchar(50)') AS DeviceName
FROM(VALUES(1,CONVERT(xml,'<root>
<person name="John">
<device name="notebook" />
<device name="xbox" />
</person>
<person name="Jane">
<device name="TV" />
</person>
<person name="Mark">
</person>
</root>')))V(ID, Content)
CROSS APPLY V.Content.nodes('root/person/device') p(d);
I have
mytable consists of one XMLTYPE column called 'RS'. RS contains:
<test>
<mycol>
<name>a</name>
<number>1</number>
<number>2</number>
<number>50</number>
<number>60</number>
</mycol>
<mycol>
<name>b</name>
<number>5</number>
<number>820</number>
<number>601</number>
</mycol>
<mycol>
<name>c</name>
<number>6</number>
<number>8</number>
<number>62</number>
</mycol>
etc...
</test>
I'm looking to run a select statement that will display ALL names and up to 2 numbers from mytable.
something like this select statement but for all rows and without calling mycol[] several times.
select a.RS.extract('/test/mycol[1]/name[1]/text()').getstringval() as Names,
a.RS.extract('/test/mycol[1]/a[1]/text()').getstringval() ||''||
a.RS.extract('/test/mycol[1]/a[2]/text()').getstringval() ||''||
a.RS.extract('/test/mycol[1]/a[3]/text()').getstringval()
as num
from mytable a;/
output should be:
Names | num
a | 1 2
b | 5 820
c | 6 8
etc...
Thanks in advance.
Xml_table and string-join may be helpful
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Input data
id year Name provid prov
1 1995 MAC 1995-11_CL236 reg 236
1 1995 MAC 1995-11_CL230 reg 230 (1)
1 1995 MAC 1995-11_CL229J reg 229J
1 1995 MAC 1995-11_CL260 reg 260
My query looks like this
select
id, year, Name, prov, provid
from
Table
for xml path ('entry'), root('legref'), elements
The above Query generating different entry for each row. But I need group by id year, name and provide single entry with different prov and provid.
<legref>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 229J</prov>
<provid>NSW_REG_1995-11_CL229J</provid>
</entry>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 230 (1)</prov>
<provid>NSW_REG_1995-11_CL230</provid>
</entry>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 236</prov>
<provid>NSW_REG_1995-11_CL236</provid>
</entry>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 260</prov>
<provid>NSW_REG_1995-11_CL260</provid>
</entry>
</legref>
Output Data:
How do I convert SQL query result to XML?
Expected result set:
<legref>
<entry>
<id>1<id>
<year>1995</year>
<Name>MAC</Name>
<prov provID="1995-11_CL230">reg 230 (1)</prov>
<prov provID="1995-11_CL236">reg 236</prov>
<prov provID="1995-11_CL260">reg 260</prov>
<prov provID="1995-11_CL229J">reg 229J</prov>
</entry>
</legref>
Try This
FIDDLE DEMO
SELECT ID, Year, Name,
(
SELECT ProvID AS 'Prov/#ProvID',Prov
FROM tbl t
WHERE ID = t.ID AND Name = t.Name
FOR XML PATH(''),TYPE
)
FROM tbl
GROUP BY ID, Year, Name
FOR XML PATH ('Entry'),ROOT('legref')
Output
<legref>
<Entry>
<ID>1</ID>
<Year>1995</Year>
<Name>MAC</Name>
<Prov ProvID="1995-11_CL236">Reg 236</Prov>
<Prov ProvID="1995-11_CL230">Reg 230</Prov>
<Prov ProvID="1995-11_CL229J">Reg 229J</Prov>
<Prov ProvID="1995-11_CL260">Reg 260</Prov>
</Entry>
</legref>
Please try below query,
SELECT id
,year
,Name
,prov
,provid
FROM TABLE
FOR XML RAW ,ELEMENTS
I have tried it for different table for same type of result.
Please check snap:
Given the following tables where T_DATA.ID = PARENT_ID or CHILD.ID
Name: T_DATA
+----+------+--------+
| ID | CODE | VALUE |
+----+------+--------+
| 1 | 3186 | value1 |
| 2 | 3186 | value2 |
| 3 | 3189 | value3 |
| 4 | 3189 | value4 |
| 5 | 3190 | value5 |
+----+------+--------+
Name: T_DATA_LINK
+-----------+----------+
| PARENT_ID | CHILD_ID |
+-----------+----------+
| 1 | 3 |
| 1 | 4 |
+-----------+----------+
I want to return an xml structure like this:
<ITEM_LIST>
<ITEM>
<CODE>3186</CODE>
<ROWS>
<ROW>
<ID>1</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value1</VALUE>
</ROW>
<ROW>
<ID>2</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value2</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<CODE>3189</CODE>
<ROWS>
<ROW>
<ID>3</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value3</VALUE>
</ROW>
<ROW>
<ID>4</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value4</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<CODE>3190</CODE>
<VALUE>value5</VALUE>
</ITEM>
</ITEM_LIST>
The ROW_INDEX is incremented by 1 for every ROW.
I need the T_DATA_LINK table to know whether an ITEM has a parent or not.
If it has a parent it means that there is more than one record with that CODE value and they need to be displayed as ROWS, otherwise it has to be displayed as a single ITEM.
UPDATE
I actually need to check the T_DATA_LINK table since there may be cases where an ITEM has a parent and only one record, but it still need to be displayed as a ROW.
#Shnugo I tried your solution, but even if now I get the correct values inside the ROWS, I get duplicates for each ITEM that has more than one record.
This is probably because I had to add to the GROUP BY the other fields I need to return with the SELECT which I didn't add to the example in order to keep it simpler.
For example, the ID need to be displayed at the ITEM level for the items which don't have any ROWS.
UPDATE 2
#Shnugo you are correct. Items 3 and 4 are the children of Item 1, but you don't see the relationship in the xml.
All the items are unique, always.
The items that are referenced in T_DATA_LINK are still unique, but are linked to each other in my application where they are displayed inside a table.
Basically the PARENT is the first column of the table and the children are the others columns.
This is the updated output I want to get.
ID should be always -1 for the items that have rows.
PARENT_CODE should be the CODE of the parent (if the item is a parent then it is equal to the CODE)
<ITEM_LIST>
<ITEM>
<ID>-1</ID>
<CODE>3186</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>1</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value1</VALUE>
</ROW>
<ROW>
<ID>2</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value2</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>-1</ID>
<CODE>3189</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>3</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value3</VALUE>
</ROW>
<ROW>
<ID>4</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value4</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>5</ID>
<CODE>3190</CODE>
<VALUE>value5</VALUE>
</ITEM>
</ITEM_LIST>
This is a new answer... Please try to put all needed information into the initial question...
DECLARE #t_data TABLE(ID INT,CODE INT,VALUE VARCHAR(100));
INSERT INTO #t_data VALUES
(1,3186,'value1')
,(2,3186,'value2')
,(3,3189,'value3')
,(4,3189,'value4')
,(5,3190,'value5');
DECLARE #t_data_link TABLE(PARENT_ID INT, CHILD_ID INT)
INSERT INTO #t_data_link VALUES
(1,3)
,(1,4);
--The CTE links the two tables and allows to handle them as one derived table
WITH Combined AS
(
SELECT d.*
,d2.CODE AS PARENT_CODE
,COUNT(*) OVER(PARTITION BY d.CODE) AS CountRows
FROM #t_data AS d
LEFT JOIN #t_data_link AS dl ON d.ID=dl.CHILD_ID
LEFT JOIN #t_data AS d2 ON dl.PARENT_ID=d2.ID
)
SELECT CASE WHEN c.CountRows>1 THEN -1 END AS ID
,CASE WHEN c.CountRows>1 THEN c.CODE END AS CODE
,CASE WHEN c.CountRows>1 THEN ISNULL(c.PARENT_CODE,c.CODE) END AS PARENT_CODE
--This part for elements with just one row per code
,(
SELECT d2.ID
,d2.CODE
,d2.VALUE
FROM #t_data AS d2
WHERE c.CODE=d2.CODE
AND c.CountRows=1
FOR XML PATH(''),TYPE
)
--This part for elements with more rows per code
,(
SELECT d2.ID
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS ROW_INDEX
,d2.VALUE
FROM #t_data AS d2
WHERE c.CODE=d2.CODE
AND c.CountRows>1
FOR XML PATH('ROW'),ROOT('ROWS'),TYPE
)
FROM Combined AS c
GROUP BY c.CODE,c.CountRows,c.PARENT_CODE
FOR XML PATH('ITEM'),ROOT('ITEM_LIST');
The result
<ITEM_LIST>
<ITEM>
<ID>-1</ID>
<CODE>3186</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>1</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value1</VALUE>
</ROW>
<ROW>
<ID>2</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value2</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>-1</ID>
<CODE>3189</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>3</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value3</VALUE>
</ROW>
<ROW>
<ID>4</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value4</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>5</ID>
<CODE>3190</CODE>
<VALUE>value5</VALUE>
</ITEM>
</ITEM_LIST>
XML will omit any NULL value. The WHERE clause in the subselects will return with NULL if there's nothing found...
Using MSSQL 2008 and XQUERY
Consider the following XML stored in a table:
<ROOT>
<WrapperElement>
<ParentElement ID=1>
<Title>parent1</Title>
<Description />
<ChildElement ID="6">
<Title>Child 4</Title>
<Description />
<StartDate>2010-01-25T00:00:00</StartDate>
<EndDate>2010-01-25T00:00:00</EndDate>
</ChildElement>
<ChildElement ID="0">
<Title>Child1</Title>
<Description />
<StartDate>2010-01-25T00:00:00</StartDate>
<EndDate>2010-01-25T00:00:00</EndDate>
</ChildElement>
<ChildElement ID="8">
<Title>Child6</Title>
<Description />
<StartDate>2010-01-25T00:00:00</StartDate>
<EndDate>2010-01-25T00:00:00</EndDate>
</ChildElement>
</ParentElement>
</WrapperElement>
</Root>
I want to decompose this xml into something like
PE!ID | PE!Title | PE!Description | CE!ID | CE!Title | CE!StartDate |...
1 | parent1 | | 6 | child 4 | 2010-... |
1 | parent1 | | 0 | child1 | 2010-... |
etc.
Note: there may be many ChildElements per ParentElement, in this example.
I've been experimenting with xquery however i've not been able to navigate through complex elements as such.
Basically, i'm trying to do the exact opposite of what FOR XML does to a table, only with a much more simplistic set of data to work with.
Any ideas on where to go next or how to accomplish this?
Thanks
How about this (I declared #input to be a XML datatype variable with your XML content - replace accordingly):
SELECT
Parent.Elm.value('(#ID)[1]', 'int') AS 'ID',
Parent.Elm.value('(Title)[1]', 'varchar(100)') AS 'Title',
Parent.Elm.value('(Description)[1]', 'varchar(100)') AS 'Description',
Child.Elm.value('(#ID)[1]', 'int') AS 'ChildID',
Child.Elm.value('(Title)[1]', 'varchar(100)') AS 'ChildTitle',
Child.Elm.value('(StartDate)[1]', 'DATETIME') AS 'StartDate',
Child.Elm.value('(EndDate)[1]', 'DATETIME') AS 'EndDate'
FROM
#input.nodes('/ROOT/WrapperElement/ParentElement') AS Parent(Elm)
CROSS APPLY
Parent.Elm.nodes('ChildElement') AS Child(Elm)
You basically iterate over all the /ROOT/WrapperElement/ParentElemet nodes (as Parent(Elm) pseudo table), and for each of those entries, you then do a CROSS APPLY for the child elements contained inside that ParentElement and pluck out the necessary information.
Should work - I hope!