SQL-Server XML-Bulk-Import and reading as table-data - sql-server

I have the following Problem:
For the XML-Import into SQL-Sever, I use this code:
DROP TABLE XMLwithOpenXML
CREATE TABLE XMLwithOpenXML
(
Id INT IDENTITY PRIMARY KEY,
XMLData XML,
LoadedDateTime DATETIME
)
INSERT INTO XMLwithOpenXML(XMLData, LoadedDateTime)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK '\\WINSER1\\proALPHA\\templates_eBus\\Test.xml', SINGLE_BLOB) AS x;
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
It works fine. But here in the XML, I don't know, what should I do:
<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
</MIME_INFO>
An user of me, needs both <MIME>-Blocks. But they are named the same!
How can I get this 8 rows contented in the 2 <Mime>-Tags? Rename is not the solution, because the XML has over 2.000.000 rows!
THX.
EDIT 16:20
Here the rest of the code above. With this tags it works fine:
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT [SUPPLIER_AID]
,REFERENCE_FEATURE_SYSTEM_NAME
,REFERENCE_FEATURE_GROUP_ID
FROM OPENXML(#hDoc, 'BMECAT/T_NEW_CATALOG/ARTICLE')
--FROM OPENXML(#hDoc, 'BMECAT/T_NEW_CATALOG/ARTICLE/ARTICLE_ORDER_DETAILS')
--## Hier werden die gewünschten Columns deklariert.
WITH
(
SUPPLIER_AID [varchar](25) 'SUPPLIER_AID'
,REFERENCE_FEATURE_SYSTEM_NAME [varchar](25) 'REFERENCE_FEATURE_SYSTEM_NAME'
,REFERENCE_FEATURE_GROUP_ID [varchar](25) 'REFERENCE_FEATURE_GROUP_ID'
)
-----------------------------EDIT 16092016 / 08:14-----------------------------
I still don't understand your code, because you use not the real table "XMLwithOpenXML". Hier is one article of ~ 20.000 in the XML:
<BMECAT>
<T_NEW_CATALOG>
<ARTICLE mode="new">
<SUPPLIER_AID>9900026005</SUPPLIER_AID>
<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube305149.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>3</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube108453.jpg</MIME_SOURCE>
<MIME_PURPOSE>others</MIME_PURPOSE>
<MIME_ORDER>4</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>application/pdf</MIME_TYPE>
<MIME_SOURCE>ube007100.pdf</MIME_SOURCE>
<MIME_PURPOSE>others</MIME_PURPOSE>
<MIME_ORDER>5</MIME_ORDER>
</MIME>
</MIME_INFO>
</ARTICLE>
</T_NEW_CATALOG>
</BMECAT>
You see there is one SUPPLIER_AID and four times a <MIME>-tag. I need only the first and the second (where normal and logo). What is in this case with the SUPPLIER_AID? I think the code has to look like:
WITH Numbered AS
(
SELECT LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
--,a.query('.') AS SUPPLIER_AID
,m.query('.') AS mime
FROM XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('BMECAT/T_NEW_CATALOG/ARTICLE/MIME_INFO/MIME') AS A(m)
)
SELECT ID
--,[SUPPLIER_AID].value('(ARTICLE)[1]','nvarchar(max)') AS SUPPLIER_AID
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','nvarchar(max)') AS MIME_ORDER
FROM Numbered
With the new code, I get this:
+-----------+--------------+-------------+-----------+
|MIME_TYPE |MIME_SOURCE |MIME_PURPOSE |MIME_ORDER |
+-----------+--------------+-------------+-----------+
|image/jpeg |ube105252.jpg |normal |1 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |logo |2 |
+-----------+--------------+-------------+-----------+
|image/jpeg |ube105252.jpg |logo |3 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |others |4 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |others |5 |
+-----------+--------------+-------------+-----------+
But what I need is something like:
+-------------+------------+------------------+--------------+-------------+
|SUPPLIER_AID | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+-------------+------------+------------------+--------------+-------------+
|9900026005 | image/jpeg | ube105252.jpg | normal | 1 |
+-------------+------------+------------------+--------------+-------------+
|9900026005 | image/jpeg | bbd372670.jpg | logo | 2 |
+-------------+------------+------------------+--------------+-------------+

Your approach with FROM OPENXML is outdated and should not be used any more. There are much better XML methods like .node(), .value(), .query() and .modify().
The way you get the XML into your table is quite OK. Once you have it there, you should continue like this:
Attention I use a declared mock-up-table to simulate your table.
DECLARE #XMLwithOpenXML TABLE(XMLData XML,LoadedDateTime DATETIME);
INSERT INTO #XMLwithOpenXML VALUES
('<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
</MIME_INFO>',GETDATE());
At this point, your XML is successfully taken into your table
The CTE "Numbered" will read all MIME elements using .nodes() in there inherent order and number them accordingly.
The SELECT pulls the actual data
WITH Numbered AS
(
SELECT LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
,m.query('.') AS mime
FROM #XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('/MIME_INFO/MIME') AS A(m)
)
SELECT ID
,LoadedDateTime
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','nvarchar(max)') AS MIME_ORDER
FROM Numbered
The result
+----+-------------------------+------------+---------------+--------------+------------+
| ID | LoadedDateTime | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+----+-------------------------+------------+---------------+--------------+------------+
| 1 | 2016-09-15 16:37:30.730 | image/jpeg | ube105252.jpg | normal | 1 |
+----+-------------------------+------------+---------------+--------------+------------+
| 2 | 2016-09-15 16:37:30.730 | image/jpeg | bbd372670.jpg | logo | 2 |
+----+-------------------------+------------+---------------+--------------+------------+
UPDATE
You did not show the full XML... With the example given above this code extracts all you might want to knwo:
WITH Numbered AS
(
SELECT Id
,LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS MimeRowNr
,a.value('#mode','nvarchar(max)') ARTICLE_MODE
,a.value('SUPPLIER_AID[1]','nvarchar(max)') AS SUPPLIER_AID
,m.query('.') AS mime
FROM XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('/BMECAT/T_NEW_CATALOG/ARTICLE') AS A(a)
CROSS APPLY a.nodes('MIME_INFO/MIME') AS B(m)
)
SELECT Id
,MimeRowNr
,LoadedDateTime
,ARTICLE_MODE
,SUPPLIER_AID
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','int') AS MIME_ORDER
FROM Numbered;
The result
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| Id | MimeRowNr | LoadedDateTime | ARTICLE_MODE | SUPPLIER_AID | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 1 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube105252.jpg | normal | 1 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 2 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | bbd372670.jpg | logo | 2 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 3 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube305149.jpg | logo | 3 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 4 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube108453.jpg | others | 4 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 5 | 2016-09-16 09:32:53.570 | new | 9900026005 | application/pdf | ube007100.pdf | others | 5 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+

Related

Logic in SQL Server to create a derived column based on comparing two comma separated columns

Want to create logic in SQL Server to create a derived column based on comparing two comma-separated columns.
Sample table data -
Create table ##table1 (ID INT Identity Primary Key, FulfillmentChannelStatus varchar(255),RoleAlternateSourcingChannel varchar (255))
insert into ##table1 values ('Filled,Open,In-process','Internal,Recruiter,Contractor')
,('Open,In-process,New','Contractor,Internal,Recruiter')
,('New,Filled','Contractor,Recruiter ')
,('Filled','Recruiter')
,('Open,New,Filled','Internal,Recruiter,Contractor')
,('Filled,Filled,Filled','Internal,Contractor,Recruiter')
,('Open ,Filled, In-proces','Contractor,Internal,Recruiter')
,('Filled','Others')
,('Cancelled,Filled','Contractor,Recruiter')
,('Cancelled, Filled, Cancel - In Process','Contractor,Recruiter,Internal')
Logic for new column--
--select * from ##tble
DECLARE #separator CHAR(1) = ','
SELECT
[Role Id],[RoleAlternateSourcingChannel],[FulfillmentChannelStatus] , [Filled fulfil] = x.value('(/root/r[sql:column("t.pos")]/text())[1]', 'VARCHAR(10)')
into ##temp FROM ##tble
CROSS APPLY (SELECT x = TRY_CAST('<root><r><![CDATA[' +
REPLACE([FulfillmentChannelStatus], #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
.query('
for $x in /root/r[text()="Filled"][1]
return count(root/r[. << $x]) + 1
').value('text()[1]','INT')) AS t(pos)
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE([RoleAlternateSourcingChannel], #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t2(x)
Scenario: I have two comma-separated columns
1 . I need to calculate values for only "Filled" values (in column [Role Alternate Sourcing Channel])
2. In 1st first row- I have a Filled value for Internal Scheduling in column ([RoleAlternateSourcingChannel]) so in the output column – it will be Internal.
3. In 2nd row – I don’t have any Filled so the output will be Null.
4. in 3rd row - I have a Filled value for Recruiter so the output will be Recruiter.
And so on…
5.In Row 6 for all value is filled so the output will be a recruiter. because preference of Recruiter>Internal>Contractor
Other than Recruiter/Internal/ Contractor all filled values will be Null.
The position of Filled value is not fixed. It can be anywhere such as - either at 1st place/position or 2nd place or in 3rd place.
Expected Output -
|+----+------------------------+----------------------------+---------------+
| ID |FulfillmentChannelStatus|RoleAlternateSourcingChannel| Filled fulfil |
+----+------------------------+----------------------------+---------------+
| 1 | Filled,Open,In-process | Internal,Recruiter,Contractor | Internal |
| 2 | Open,In-process,New | Contractor,Internal,Recruiter | NULL |
| 3 | New,Filled | Contractor,Recruiter | Recruiter |
| 4 | Filled | Recruiter | Recruiter |
| 5 | Open,New,Filled | Internal,Recruiter,Contractor | Contractor |
| 6 | Filled,Filled,Filled | Internal,Contractor,Recruiter | Recruiter |
| 7 | Open ,Filled, In-process| Contractor,Internal,Recruiter | Internal |
| 8 | Filled | Others | Null
| 9 | Cancelled, Filled, Cancel - In Procecess|Contractor,Internal,Recruiter | Internal
| 10| Cancelled, Filled| Internal,Recruiter| Recruiter
+----+------------------------+-------+--------+----------------------------+
**Question:** I tried Query2, For all other cases it is working fine now but for Row 9 and 10 O/P is Null but it should be Internal and Recruiter respectively.
A minimal reproducible example ##1-4 is not provided.
Shooting from the hip.
Please try the following solution based on XQuery.
XML and XQuery data model is based on ordered sequences, exactly what we need.
You moved the goalposts in the middle of the game.
I made just the "Recruiter","Internal","Contractor" as a legitimate
values for the RoleAlternateSourcingChannel column. Everything
else is filtered out.
I don't see any easy way to handle the preference of
Recruiter>Internal>Contractor for the row #6.
SQL #1
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, FulfillmentChannelStatus VARCHAR(255), RoleAlternateSourcingChannel VARCHAR(255));
INSERT INTO #tbl (FulfillmentChannelStatus, RoleAlternateSourcingChannel) VALUES
('Filled,Open,In-process', 'Internal,Recruiter,Contractor'),
('Open,In-process,New', 'Contractor,Internal,Recruiter'),
('New,Filled', 'Contractor,Recruiter'),
('Filled', 'Recruiter'),
('Open,New,Filled', 'Internal,Recruiter,Contractor'),
('Filled,Filled,Filled', 'Internal,Contractor,Recruiter'),
('Open,Filled,In-process', 'Contractor,Internal,Recruiter'),
('Filled', 'Others');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
SELECT tbl.*
, Result = x.value('(/root/r[sql:column("t.pos")]/text())[1]', 'VARCHAR(10)')
FROM #tbl AS tbl
CROSS APPLY (SELECT x = TRY_CAST('<root><r><![CDATA[' +
REPLACE(FulfillmentChannelStatus, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
.query('
if (count(/root/r[text()="Filled"]) eq 1) then
for $x in /root/r[text()="Filled"]
return count(root/r[. << $x]) + 1
else ()
').value('text()[1]','INT')) AS t(pos)
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(RoleAlternateSourcingChannel, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML).query('<root>
{
for $x in /root/r[text()=("Recruiter","Internal","Contractor")]
return $x
}
</root>
')) AS t2(x);
Output
+----+--------------------------+-------------------------------+------------+
| ID | FulfillmentChannelStatus | RoleAlternateSourcingChannel | Result |
+----+--------------------------+-------------------------------+------------+
| 1 | Filled,Open,In-process | Internal,Recruiter,Contractor | Internal |
| 2 | Open,In-process,New | Contractor,Internal,Recruiter | NULL |
| 3 | New,Filled | Contractor,Recruiter | Recruiter |
| 4 | Filled | Recruiter | Recruiter |
| 5 | Open,New,Filled | Internal,Recruiter,Contractor | Contractor |
| 6 | Filled,Filled,Filled | Internal,Contractor,Recruiter | NULL |
| 7 | Open,Filled,In-process | Contractor,Internal,Recruiter | Internal |
| 8 | Filled | Others | NULL |
+----+--------------------------+-------------------------------+------------+
SQL #2
DB fiddle
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, FulfillmentChannelStatus VARCHAR(255), RoleAlternateSourcingChannel VARCHAR(255));
INSERT INTO #tbl (FulfillmentChannelStatus, RoleAlternateSourcingChannel) VALUES
('Filled,Open,In-process', 'Internal,Recruiter,Contractor'),
('Open,In-process,New', 'Contractor,Internal,Recruiter'),
('New,Filled', 'Contractor,Recruiter'),
('Filled', 'Recruiter'),
('Open,New,Filled', 'Internal,Recruiter,Contractor'),
('Filled,Filled,Filled', 'Internal,Contractor,Recruiter'),
('Open,Filled,In-process', 'Contractor,Internal,Recruiter'),
('Filled', 'Others'),
('Cancelled,Filled','Contractor,Recruiter'),
('Cancelled, Filled, Cancel - In Process','Contractor,Recruiter,Internal');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
;WITH rs AS
(
SELECT ID, x
FROM #tbl
CROSS APPLY (SELECT TRY_CAST('<root>' +
'<source><r><![CDATA[' + REPLACE(REPLACE(FulfillmentChannelStatus,SPACE(1),''), #separator, ']]></r><r><![CDATA[') +
']]></r></source>' +
'<target><r><![CDATA[' + REPLACE(REPLACE(RoleAlternateSourcingChannel,SPACE(1),''), #separator, ']]></r><r><![CDATA[') +
']]></r></target>' +
'</root>' AS XML).query('<root>
{
for $x in /root/source/r
let $pos := count(root/source/r[. << $x]) + 1
return <r>
<s>{data($x)}</s><t>{data(/root/target/r[$pos])}</t>
</r>
}
</root>')) AS t(x)
), cte AS
(
SELECT ID
, c.value('(s/text())[1]', 'VARCHAR(30)') AS source
, c.value('(t/text())[1]', 'VARCHAR(30)') AS [target]
FROM rs
CROSS APPLY x.nodes('/root/r') AS t(c)
), cte2 AS
(
SELECT *
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY
CASE [target]
WHEN 'Recruiter' THEN 1
WHEN 'Internal' THEN 2
WHEN 'Contractor' THEN 3
END) AS seq
FROM cte
WHERE source = 'Filled'
AND [target] IN ('Recruiter','Internal','Contractor')
)
SELECT t.*
, c.[target] --, c.seq
FROM #tbl AS t
LEFT OUTER JOIN cte2 AS c ON c.ID = t.ID
WHERE c.seq = 1 OR c.seq is NULL
ORDER BY t.ID;
Output
+----+----------------------------------------+-------------------------------+------------+
| ID | FulfillmentChannelStatus | RoleAlternateSourcingChannel | target |
+----+----------------------------------------+-------------------------------+------------+
| 1 | Filled,Open,In-process | Internal,Recruiter,Contractor | Internal |
| 2 | Open,In-process,New | Contractor,Internal,Recruiter | NULL |
| 3 | New,Filled | Contractor,Recruiter | Recruiter |
| 4 | Filled | Recruiter | Recruiter |
| 5 | Open,New,Filled | Internal,Recruiter,Contractor | Contractor |
| 6 | Filled,Filled,Filled | Internal,Contractor,Recruiter | Recruiter |
| 7 | Open,Filled,In-process | Contractor,Internal,Recruiter | Internal |
| 8 | Filled | Others | NULL |
| 9 | Cancelled,Filled | Contractor,Recruiter | Recruiter |
| 10 | Cancelled, Filled, Cancel - In Process | Contractor,Recruiter,Internal | Recruiter |
+----+----------------------------------------+-------------------------------+------------+

Get XML namespace text via SQL query

I am working with XML data on an SQL Server. The (exemplary) SQL looks as follows:
<Document xmlns="urn:iso:std:iso:20022:some:test:xmlns">
<Testnode>a</Testnode>
</Document>
The XML is available in a table with a column named <fata of type XML.
My question is: How can I create a SELECT query that shows the text of the namespace in one column?
The expected output should be:
+----------------------------------------+
| xmlns |
+----------------------------------------+
| urn:iso:std:iso:20022:some:test:xmlns |
+----------------------------------------+
The result column should be a character string (no XML).
So far I have tried this query, however the result is NULL:
SELECT Data.value('(./Document)[1]','nvarchar(max)') AS xmlns
FROM xmltable
You can try something along this:
DECLARE #xml XML=
N'<Document xmlns="urn:iso:std:iso:20022:some:test:xmlns">
<Testnode>a</Testnode>
</Document>';
--The XQuery-function namespace-uri() takes a singleton and returns its namespace uri
SELECT #xml.value('namespace-uri((/*:Document)[1])','nvarchar(max)');
As the <Document> element is living within the default namespace itself, we would have to know the namespace in advance in order to declare it. But - luckily - we can use the wildcard with *:.
Another option - one of the rare cases - is the usage of the outdated FROM OPENXML:
Try this:
DECLARE #xml XML=
N'<Document xmlns="urn:iso:std:iso:20022:some:test:xmlns">
<Testnode>a</Testnode>
</Document>';
DECLARE #docHandle INT;
EXEC sp_xml_preparedocument #docHandle OUTPUT, #xml;
SELECT * FROM OPENXML (#docHandle, '/*',1);
EXEC sp_xml_removedocument #docHandle;
This returns the complete XML with a lot of meta-data:
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| id | parentid | nodetype | localname | prefix | namespaceuri | datatype | prev | text |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 0 | NULL | 1 | Document | NULL | urn:iso:std:iso:20022:some:test:xmlns | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 2 | 0 | 2 | xmlns | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 4 | 2 | 3 | #text | NULL | NULL | NULL | NULL | urn:iso:std:iso:20022:some:test:xmlns |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 3 | 0 | 1 | Testnode | NULL | urn:iso:std:iso:20022:some:test:xmlns | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 5 | 3 | 3 | #text | NULL | NULL | NULL | NULL | a |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+

Split columns from single table and join as multiple columns on another

I want to split a column in 1 table into multiple columns in another table
Let's say I want to join the FoodAttributeTable to the FoodTable:
FoodTable
+------------+-------+
| Food | Price |
+------------+-------+
| Strawberry | 10 |
| Broccoli | 25 |
+------------+-------+
FoodAttributeTable
+------------+---------------+----------------+
| Food | AttributeName | AttributeValue |
+------------+---------------+----------------+
| Strawberry | Vitamin | C |
| Strawberry | Weight | 15g |
| Strawberry | Color | Red |
| Broccoli | Vitamin | B |
| Broccoli | Weight | 70g |
| Broccoli | Color | Green |
+------------+---------------+----------------+
Table to be:
FoodTable
+------------+-------+---------+--------+-------+
| Food | Price | Vitamin | Weight | Color |
+------------+-------+---------+--------+-------+
| Strawberry | 10 | C | 15g | Red |
| Broccoli | 25 | B | 70g | Green |
+------------+-------+---------+--------+-------+
you can use a simple PIVOT query like below.
Check out official MSDN documentation on PIVOT.
select
Food,Price,Vitamin,Weight,Color
from
(
select f.Food,f.Price,
AttributeName ,AttributeValue
from
FoodTable f join
FoodAttributeTable fat on
f.Food=fat.Food
)s
pivot
(max(AttributeValue) for AttributeName in (Vitamin,Weight,Color))
p
Also here's a live demo
This is a Dynamic Pivot Query. Therefore, I would only recommend this if your FoodAttributeTable attributes are dynamics.
Create Tables and Insert Data
CREATE TABLE FoodAttributeTable(
Food nvarchar(50) NULL,
AttributeName nvarchar(50) NULL,
AttributeValue nvarchar(50) NULL
)
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Strawberry', N'Vitamin', N'C')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Strawberry', N'Weight', N'15g')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Strawberry', N'Color', N'Red')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Broccoli', N'Vitamin', N'B')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Broccoli', N'Weight', N'70g')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Broccoli', N'Color', N'Green')
CREATE TABLE FoodTable(
Food nvarchar(50) NULL,
Price decimal(18, 0) NULL
)
INSERT FoodTable (Food, Price) VALUES (N'Strawberry', N'10')
INSERT FoodTable (Food, Price) VALUES (N'Broccoli', N'25')
Query
DECLARE #colsValues AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename(fat.attributename)
FROM foodattributetable fat
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #query AS NVARCHAR(max) = 'SELECT *
FROM (SELECT *
FROM foodattributetable
PIVOT(Max(attributevalue)
FOR attributename IN ('+ #colsValues +')) piv) fat
INNER JOIN foodtable tb
ON tb.food = fat.food';
EXECUTE(#query)
Output
+------------+-------+---------+--------+------------+-------+
| Food | Color | Vitamin | Weight | Food | Price |
+------------+-------+---------+--------+------------+-------+
| Broccoli | Green | B | 70g | Broccoli | 25 |
| Strawberry | Red | C | 15g | Strawberry | 10 |
+------------+-------+---------+--------+------------+-------+
Demo: http://rextester.com/ATZF46215

Get Value from XML attribute in SQL Server 2008 Using OPENXML

I'm trying to extract a value from my XML and seem to be struggling. Hope someone can help
here is my XML
'<Transfer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Products>
<Product TUT="true" ID="38319223176264031724">
<Identifier>38319223176264031724</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>23.100</NetWeight>
<GrossWeight>23.684</GrossWeight>
<SerialNumber>317</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-09-20T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-09-19T00:00:00</Attribute>
</Product>
<Product TUT="true" ID="28319219766306010024">
<Identifier>28319219766306010024</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>19.700</NetWeight>
<GrossWeight>20.284</GrossWeight>
<SerialNumber>100</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-11-01T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-10-31T00:00:00</Attribute>
</Product>
</Products>
</Transfer>'
What I want to extract are the Identifier, ProductItemCode, NetWeight, GrossWeight, AND the Attribute Values of PackDate and SlgrDate.
I can easily get all of the fields EXCEPT for Attribute Values of PackDate and SlgrDate
Here is my code for the fields
if OBJECT_ID('tempdb..#XmlImportTest') is not null
drop table #XmlImportTest
CREATE TABLE #XmlImportTest(
xmlFileName VARCHAR(300) NOT NULL,
xml_data XML NOT NULL
)
GO
DECLARE #xmlFileName VARCHAR(3000)
SELECT #xmlFileName = 'K:\Upload\CSNXML\WaybillXml.xml'
--– dynamic sql is just so we can use #xmlFileName variable in OPENROWSET
EXEC('INSERT INTO #XmlImportTest(xmlFileName, xml_data)
SELECT ''' + #xmlFileName + ''', xmlData
FROM(
SELECT *
FROM OPENROWSET (BULK ''' + #xmlFileName + ''', SINGLE_BLOB) AS XMLDATA
) AS FileImport (XMLDATA)
')
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
select #xml = (SELECT xml_data from #XmlImportTest)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT Identifier as barcode,ProductItemCode as standpack,SerialNumber, NetWeight netwt_ind, GrossWeight grosswt_ind
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2)
)
exec sp_xml_removedocument #hDoc
the xml file contains the same as the sample xml provided
Now I have no idea how to get the value out of the Attributes for each product.
I am running this in SQL SERVER 2008
Use the optional ColPattern to specify the XPath to the node you want.
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (
Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2),
PackDate datetime 'Attribute[#Name = "PackDate"]',
PlantID int 'Attribute[#Name = "PlantID"]',
SlgrDate datetime 'Attribute[#Name = "SlgrDate"]'
)
FROM OPENXML is outdated and should not be used anymore (rare exceptions exist)
Try to use the up-to-date XML-type-methods:
SELECT p.value(N'#TUT',N'bit') AS TUT
,p.value(N'#ID',N'nvarchar(max)') AS ID
,p.value(N'(Identifier/text())[1]',N'nvarchar(max)') AS Identifier
,p.value(N'(ProductItemCode/text())[1]',N'int') AS ProductItemCode
,p.value(N'(Qty/text())[1]',N'int') AS Qty
,p.value(N'(NetWeight/text())[1]',N'decimal(14,4)') AS NetWeight
,p.value(N'(SerialNumber/text())[1]',N'int') AS SerialNumber
,p.value(N'(ECertItemNumber/text())[1]',N'int') AS ECertItemNumber
,p.value(N'(Markets/#Type)[1]',N'nvarchar(max)') AS Markets_Type
,m.value(N'text()[1]',N'nvarchar(max)') AS Markets_Market
,p.value(N'(Attribute[#Name="PackDate"]/text())[1]',N'datetime') AS PackDate
,p.value(N'(Attribute[#Name="PlantID"]/text())[1]',N'int') AS PlantID
,p.value(N'(Attribute[#Name="SlgrDate"]/text())[1]',N'datetime') AS SlgrDate
FROM #xml.nodes(N'Transfer/Products/Product') AS A(p)
CROSS APPLY a.p.nodes(N'Markets/Market') AS B(m);
The result
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| TUT | ID | Identifier | ProductItemCode | Qty | NetWeight | SerialNumber | ECertItemNumber | Markets_Type | Markets_Market | PackDate | PlantID | SlgrDate |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | EU | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | US | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | EU | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | US | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
Hint: If you do not need the <Markets> as 1:n-relation (here doubling your resultset!), just remove the CROSS APPLY and the line starting with m.value.

T-SQL Transposing column headers into Rows

I want to transpose my table.
I have simple 'Person' table as shown below.
+---+----------+------------+------------+----------------------+
|ID | Person | BirthDate | Phone | Email |
+---+----------+------------+------------+----------------------+
| 1 | Tom | 1985-11-08 | 1111111111 | tom#somedomain.com |
+---+----------+------------+------------+----------------------+
| 2 | Dick | 1982-02-24 | 2222222222 | dick#otherdomain.com |
+---+----------+------------+------------+----------------------+
| 3 | Harry | 1986-04-17 | 3333333333 | harry#thatdomain.com |
+---+----------+------------+------------+----------------------+
And I want this table to be transposed like below.
+-----------+--------------------+----------------------+----------------------+
| Key | Value1 | Value2 | Value3 |
+-----------+--------------------+----------------------+----------------------+
| ID | 1 | 2 | 3 |
+-----------+--------------------+----------------------+----------------------+
| Person | Tom | Dick | Harry |
+-----------+--------------------+----------------------+----------------------+
| BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+-----------+--------------------+----------------------+----------------------+
| Phone | 1111111111 | 2222222222 | 3333333333 |
+-----------+--------------------+----------------------+----------------------+
| Email | tom#somedomain.com | dick#otherdomain.com | harry#thatdomain.com |
+-----------+--------------------+----------------------+----------------------+
I am using MS SQL server 2008 R2.
Try this.. First u need to unpivot the columns using Cross apply to get the data in single row. Then pivot that row to get the result.
CREATE TABLE #tt
(ID INT,Person VARCHAR(50),BirthDate DATE,
Phone BIGINT,Email VARCHAR(50)
)
INSERT INTO #tt
VALUES (1,'Tom','1985-11-08',1111111111,'tom#somedomain.com' ),
( 2,'Dick','1982-02-24',2222222222,'dick#otherdomain.com'),
( 3,'Harry ','1986-04-17',3333333333,'harry#thatdomain.com' )
SELECT [key],
Max([value1]) [value1],
Max([value2]) [value2],
Max([value3]) [value3]
FROM (SELECT 'value' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES ('ID',
CONVERT(VARCHAR(50), ID)),
('Person',Person),
('BirthDate',CONVERT(VARCHAR(50), BirthDate)),
('Phone',CONVERT(VARCHAR(50), Phone)),
('Email',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN([value1],[value2],[value3])) piv
GROUP BY [key]
DYNAMIC VERSION
Declare #cols varchar(max)='',#aggcols varchar(max)='',#sql nvarchar(max)
SELECT #cols+= ',value' + CONVERT(VARCHAR(30), id)
FROM #tt
SELECT #aggcols+= ',max([value' + CONVERT(VARCHAR(30), id) +']) value' + CONVERT(VARCHAR(30), id)
FROM #tt
select #cols= right(#cols,LEN(#cols)-1)
select #aggcols =right(#aggcols,LEN(#aggcols)-1)
set #sql = 'SELECT [key],
'+#aggcols+'
FROM (SELECT ''value'' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES (''ID'',CONVERT(VARCHAR(50), ID)),
(''Person'',Person),
(''BirthDate'',CONVERT(VARCHAR(50), BirthDate)),
(''Phone'',CONVERT(VARCHAR(50), Phone)),
(''Email'',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN('+#cols+')) piv
GROUP BY [key] '
execute sp_executesql #sql
OUTPUT
+----------+--------------------+---------------------+----------------------+
|key | value1 | value2 | value3 |
+----------+--------------------+---------------------+----------------------+
|BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+----------+--------------------+---------------------+----------------------+
|Email | tom#somedomain.com |dick#otherdomain.com | harry#thatdomain.com |
+----------+--------------------+---------------------+----------------------+
|ID | 1 | 2 | 3 |
+----------+--------------------+---------------------+----------------------+
|Person | Tom | Dick | Harry |
+----------+--------------------+---------------------+----------------------+
|Phone | 1111111111 | 2222222222 | 3333333333 |
+----------+--------------------+---------------------+----------------------+

Resources