How select XML fields node for all rows - sql

I have a table like this :
YEAR int,
Fields XML
My XML column has this structure for all rows but with different values:
How I can get this result:
YEAR ID NAME LASTNAME
---------------------------------------------------
2011 1000 Nima Agha
2011 1001 Begha Begha
2011 1002 Jigha Jigha
2011 1003 Aba Aba
2012 1034 AAA BBB
...
thanks

How about this:
SELECT
Year,
E.P.value('(ID)[1]', 'INT') AS 'ID',
E.P.value('(Name)[1]', 'VARCHAR(50)') AS 'Name',
E.P.value('(LastName)[1]', 'VARCHAR(50)') AS 'LastName'
FROM
dbo.YourTable
CROSS APPLY
Fields.nodes('/Employees/Person') AS E(P)
You're basically selecting Year from the base table and then extracting each <Person> node from the Fields column into an "inline XML table" called E with a single XML column called P (you can choose whatever names you like for those) that you again query and extract the individual elements from .

Related

How to split more than one comma separated column as a separate row in SQL using CROSS APPLY

I have one table having following rows
Name
Phones
Courses
ABC
123, 456
HTML, Java
XYZ
321, 654
PHP, CSS
now I want to write a SELECT query to get these comma separated Phone and Courses as a separate row. By using this SELECT query I yield results till Phone column but stuck at 2nd column. Select query is
SELECT
Name,
value phone,
courses
FROM
tblName
CROSS APPLY STRING_SPLIT(phones, ',');
This query shows me following results:
Name
Phones
Courses
ABC
123
HTML, Java
ABC
456
HTML, Java
XYZ
321
PHP, CSS
XYZ
654
PHP, CSS
Please help me to split Courses column as like Phones and want to yield following results:
Name
Phones
Courses
ABC
123
HTML
ABC
456
HTML
ABC
123
Java
ABC
456
Java
XYZ
321
PHP
XYZ
654
PHP
XYZ
321
CSS
XYZ
654
CSS
Since - according to your description - you used CROSS APPLY and your query was successfully executed, this means you are using a SQL Server DB, not MY SQL. You can do two CROSS APPLY to get your expected result. This will produce exactly the outcome you have shown in your question:
SELECT name, phone, value courses FROM
(SELECT name, value phone, courses
FROM tblName CROSS APPLY STRING_SPLIT(phones, ',')) x
CROSS APPLY STRING_SPLIT(courses, ',')
ORDER BY name, courses, phone;
You can verify this here: db<>fiddle
But this is very risky and you really should avoid such comma-separated contents in one column. I highly recommend to create separate columns for the different values in future.
Well I found the solution myself.
here is the working query:
SELECT
t1.name,
t1.phone,
value course
FROM
(SELECT
first_name,
value phone,
courses
FROM
tblName
CROSS APPLY STRING_SPLIT(phones, ',')) t1
CROSS APPLY STRING_SPLIT(t1.courses, ',');
Thanks everyone.

Extract multi-value field in XML format in SQL

I'm currently working with a database that stores XML record for all of its field, please see below example. Lets name the table CUSTOMER table.
customer table
------------------------------------------------------
| RECID | XMLRECORD |
| 1 | <row id='1' xml:space="preserve"><c1>... |
| 2 | <row id='2' xml:space="preserve"><c1>... |
| 3 | <row id='3' xml:space="preserve"><c1>... |
------------------------------------------------------
All of the record of each customer is stored in 1 field called XMLRECORD, below is one example of XML RECORD of a customer.
<row id="1" xml:space="preserve">
<c1>James</c1>
<c2>Anderson</c2>
<c3>25</c3>
<c4>District 2 1657</c4>
<c4 m="2">Riverside Drive Redding</c4>
<c4 m="3">California, USA</c4>
</row>
Where c1 would be the customer's first name, c2 for last name, c3 for age and c4 would be the customer's address.
To query or extract values for each column, I usually use .value function to extract and return single value.
SELECT XMLRECORD.value('(/row/c4)[1]','NVARCHAR(20)') as ADDRESS
FROM CUSTOMER
Now my problem is this function only returns a single value, what I want is to return all the values under c4, which is multi value field. Can someone advise a way to do this?
Initiating the table
declare #xml as table
(
recid int,
xmlrecord xml
)
insert into #xml
values
( 1 , '<row id="1" xml:space="preserve">
<c1>James</c1>
<c2>Anderson</c2>
<c3>25</c3>
<c4>District 2 1657</c4>
<c4 m="2">Riverside Drive Redding</c4>
<c4 m="3">California, USA</c4>
</row>' )
DECLARE #XMLRECORD as xml =
'<row id="1" xml:space="preserve">
<c1>James</c1>
<c2>Anderson</c2>
<c3>25</c3>
<c4>District 2 1657</c4>
<c4 m="2">Riverside Drive Redding</c4>
<c4 m="3">California, USA</c4>
</row>' ;
Using .nodes functionality to fetch all the nodes
SELECT T.C.value('.','NVARCHAR(1000)') as c4_nodes FROM #XMLRECORD.nodes('(/row/c4)') as T(C)
Output -
c4_nodes
---------
District 2 1657
Riverside Drive Redding
California, USA
Since this fetches multiple records, using stuff command to concatenate the rows
SELECT
recid
,STUFF((
SELECT ',' + T.C.value('.','NVARCHAR(1000)')
FROM XMLRECORD.nodes('(/row/c4)') as T(C)
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') c4
FROM #xml
Output -
recid | c4
-----------
1 District 2 1657,Riverside Drive Redding,California, USA
c4 should not repeat multiple time. data should be stored in single node. For all node value, you should use [*] to get all node value.
You can try something like this
SELECT Tmp.record.value('.','NVARCHAR(20)')
FROM [customer]
CROSS APPLY [XMLRECORD].nodes('/row/c4') as Tmp(record)

Grouping values under elements FOR XML

I am trying to format the output of a query using FOR XML in SQL Server 2012.
Each PART_NO can have a varying number of SUPPLIER_PART_NUMBER's mapped to it.
The table has data in the following format.
PART_NO SUPPLIER_PART_NO
------- ----------------
AAA 1
AAA 2
BBB 3
BBB 4
BBB 5
The desired output is as follows where part AAA has two supplier part numbers and part BBB has three supplier part numbers, and the supplier part numbers are nested below the part number.
<root>
<item PartNo ="AAA">
<mpn>1</mpn>
<mpn>2</mpn>
</item>
<item PartNo ="BBB">
<mpn>3</mpn>
<mpn>4</mpn>
<mpn>5</mpn>
</item>
</root>
The closest I can get is below, but this does not group the mpn under PartNo:
SELECT
[PART_NO] as 'item/#PartNo',
[SUPPLIER_PART_NO] as 'mpn'
FROM
[dbo].[supplier_part_mapping2]
ORDER BY
PART_NO
FOR XML PATH('') , ROOT('root');
Thank you in advance
Try this:
SELECT
p1.PART_NO as 'item/#PartNo',
(SELECT
SUPPLIER_PART_NO AS 'mpn'
FROM
[dbo].[supplier_part_mapping2] p2
WHERE
p1.PART_NO = p2.PART_NO
FOR XML PATH(''), TYPE) AS 'item'
FROM
[dbo].[supplier_part_mapping2] p1
GROUP BY
PART_NO
ORDER BY
PART_NO
FOR XML PATH('') , ROOT('root');
This should produce:
You basically need to group by the PART_NO so that you get only one <item> entry for each distinct PART_NO, and you need to grab the "sub-elements" as a subquery to list them all together under one parent node.

To update a column in XML Format Selecting data SQL Table using T-SQL

I have data in a SQL Table and I want to query some columns of the existing data in XML Format(using For XML ) or any best practice and then update the XML FOrmat into a column in the same table with XML datatype .If you notice the example below , the Node SALARY is not a column name and its a constant value. ( In this sample there are 2 AMT columns as AMT1 and AMT2 which might vary as AMT3 sometimes) and accordingly the SALARY NODE Should also increment with the next number .If any one advice how to approach it would be of great help.
The format of my data is :
EMPNAME YEAR MONTH AMT1 AMT2
smith 2013 jan 5000 6000
Ray 2014 feb 4000 5000
Jones 2013 apr 6000 3000
the XML format I want is :
<EMPLOYEE>
<EMPNAME>Smith</EMPNAME>
<SALARYDETAILS>
<SALARY>1<SALARY>
<AMOUNT>5000</AMOUNT>
<SALARY>2<SALARY>
<AMOUNT>6000</AMOUNT>
</SALARYDETAILS>
</EMPLOYEE>
<EMPLOYEE>
<EMPNAME>Ray</EMPNAME>
<SALARYDETAILS>
<SALARY>1<SALARY>
<AMOUNT>4000</AMOUNT>
<SALARY>2<SALARY>
<AMOUNT>5000</AMOUNT>
</SALARYDETAILS>
</EMPLOYEE>
I tried the basic SQL like this but not sure how to add nodes in between which are not sql columns like 'SALARYDETAILS'
SELECT EMPNAME ,AMT1,AMT2 FROM EMPLOYEE
FOR XML RAW (''), ROOT ('EMPLOYEE') - This SQL also gives error as Empty Tags cannot be passed
.
Thanks Mikael, That was very helpful ,I could get the idea on specifying dummy nodes , but the requirement is , the number of amount nodes might vary depending on data and so the SALARY node will also depend on that .. like for some records it could be just one AMT1, some might have 3 AMT columns.. so it could vary and accordingly and so the query has to be built dynamically . I could even use a stored procedure . Thanks ..
Hi , Any idea how to concatenate 2 xml variables in a Stored Procedure.
If I understand what you want you could just specify the salary nodes as constants in the query.
select E.EMPNAME,
1 as 'SALARYDETAILS/SALARY',
E.AMT1 as 'SALARYDETAILS/AMOUNT',
2 as 'SALARYDETAILS/SALARY',
E.AMT2 as 'SALARYDETAILS/AMOUNT'
from EMPLOYEE as E
for xml path('EMPLOYEE')

Query (column) that lacks a root node

I am supposed to be extracting data from an XML column in sql server 2012. Some values for this column will have multiple nodes. Unfortunately, the XML does not have a root node, and so using the CROSS APPLY does not seem to work.
Simplified example:
<header><msg_type>TYPE_ONE</msg_type>
<status><status_1>aaaa</status_1><status_2>bbbb</status_2>
<node_1><customerID>1234</customerID><zipcode>11111</zipcode>...</node_1>
<node_2><customerID>1234</customerID><ordernum>12345</ordernum><data2>A</ordernum>...</node_2>
<node_2><customerID>1234</customerID><ordernum>34567></ordernum><data2>B</ordernum>...</node_2>
<node_3><customerID>1234</customerID><delivery>2014-05-05 14:00:00></delivery>...</node_3>
<node_1><customerID>ABCD</customerID><zipcode>12345</zipcode>...</node_1>
<node_2><customerID>ABCD</customerId><ordernum>123536</ordernum><data2>C</ordernum>...</node_2>
<node_3><customerID>ABCD</customerID><delivery>2014-05-05 16:00:00>...</node_3>
.
.
.
(... = more elements)
Here's an example using CROSS APPLY against one of the multi-node types:
select
t.InfoXML.value( '/node_1/customerID)[1]', 'varchar(50)' ) as CustomerId
, CA.Det.value( '(/node_2/ordernum)[1]', 'varchar(20)') as OrderNumber
, CA.Det.value( '(/node_2/data2)[1]', 'varchar(5)' ) as Data2
from TableUnderTest as t
CROSS APPLY t.InfoXML.nodes( '/node_2') as CA(Det)
where t.InfoXML.value( '(/header/msg_type)[1]', 'varchar(20)') = 'TYPE_ONE'
This had results
CustomerID OrderNumber Data2
================================
1234 12345 A
1234 12345 A
1234 12345 A
My current thought is to create a temporary table and insert the XML fields (that match the WHERE clause) after wrapping the XML value in a root node, and then trying to get the data from the temporary table. My current effort to set the first part up:
Declare #Rooted Table (Rec XML);
insert into #Rooted(Rec)
(
select (convert (XML, '<root>', + convert(varchar(MAX),
t.XmlData.query('./') + '<\root>')) as Rec
from TableUnderTest t
where t.XmlData.value( '(/header/msg_type)[1]', 'varchar(20)' ) = 'TYPE_ONE'
)
Right now, the above gives a syntax error.
What I want for output is something as follows:
CustomerID ZipCode OrderNumber Data2 Delivery status2
-------------------------------------------------------------------
1234 11111 12345 A 2014-05-05 14:00:00 aaaa
1234 11111 34567 B 2014-05-05 14:00:00 aaaa
ABCD 12345 123456 D 2014-05-05 15:00:00 aaaa
What would be the best approach to take? (This is for testing, not production, so performance is not critical.) I've only been learning to write sql queries for XML for the last month, so perhaps I'm overlooking something. It appears the critical issue is the lack of a root node for the XML, but how do I work around it?