Apple Health XML to SQL Server - sql-server

I want to process XML data I've imported from exported Apple Health XML file.
The XML data is stored in an import table and I'm trying to use sp_xml_preparedocument to prepare the document and query the data.
The approach described below worked for another part of the XML file, specifically activity summary. Now I receive the following error:
Msg 6603, Level 16, State 2, Line 23
XML parsing error: NodeTest expected here.
#-->[<--type]
The examples given by Microsoft in their documentation seems to be working on a differently structured data, so not sure how or if I could adapt one of their examples to my needs.
I have tried the following:
DECLARE #XML AS XML;
DECLARE #hDoc AS INT;
SELECT #XML = XMLData FROM [Data].AppleImport;
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT [type],
sourceName,
sourceVersion,
unit,
creationDate,
startDate,
endDate,
[value]
FROM OPENXML(#hDoc, 'HealthData/Record')
WITH
(
[type] NVARCHAR(50) '#[type]',
sourceName NVARCHAR(50) '#sourceName',
sourceVersion NVARCHAR(50) '#sourceVersion',
unit NVARCHAR(50) '#unit',
creationDate NVARCHAR(50) '#creationDate',
startDate NVARCHAR(50) '#startDate',
endDate NVARCHAR(50) '#endDate',
[value] NVARCHAR(50) '#[value]'
)
EXEC sp_xml_removedocument #hDoc;
The XML Data structure (extracted parts for brewity):
<HealthData locale="nb_NO">
<ExportDate value="2020-09-03 12:10:19 +0200"/>
<Me HKCharacteristicTypeIdentifierDateOfBirth="1988-01-01" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexMale" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet"/>
<Record type="HKQuantityTypeIdentifierHeight" sourceName="amsten sin iPhone" sourceVersion="13.0" unit="cm" creationDate="2019-09-23 15:19:48 +0200" startDate="2019-09-23 15:19:48 +0200" endDate="2019-09-23 15:19:48 +0200" value="188"/>
<Record type="HKQuantityTypeIdentifierHeight" sourceName="Helse" sourceVersion="11.4.1" unit="cm" creationDate="2018-07-31 21:42:50 +0200" startDate="2018-07-31 21:42:50 +0200" endDate="2018-07-31 21:42:50 +0200" value="188"/>
<MetadataEntry key="HKWasUserEntered" value="1"/>
</Record>
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="amsten sin Apple Watch" sourceVersion="5.3.1" device="<<HKDevice: 0x2834d03c0>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:5.3.1>" unit="count/min" creationDate="2019-09-15 06:38:32 +0200" startDate="2019-09-15 06:30:08 +0200" endDate="2019-09-15 06:30:08 +0200" value="49">
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="1"/>
</Record>
</HealthData>
Any input on how to proceede or good reading materials are most appreciated.

When using valid XML also try using the following OPENXML statement:
SELECT [type],
sourceName,
sourceVersion,
unit,
creationDate,
startDate,
endDate,
[value]
FROM OPENXML(#hDoc, 'HealthData/Record')
WITH
(
[type] NVARCHAR(50) '#type',
sourceName NVARCHAR(50) '#sourceName',
sourceVersion NVARCHAR(50) '#sourceVersion',
unit NVARCHAR(50) '#unit',
creationDate NVARCHAR(50) '#creationDate',
startDate NVARCHAR(50) '#startDate',
endDate NVARCHAR(50) '#endDate',
[value] NVARCHAR(50) '#value'
)
Note that '#[type]' and '#[value]' have changed to '#type' and '#value'.

Related

How to parse xml data in sql server dynamically

I am trying to get a node value of 'customer' but I need to pick this node dynamically and get the value of it because I am trying to get primary key node value and I will get that primary key from other table for now I have set the variable #primary ='CUSTOMER' but I am getting error like
The data types varchar and xml are incompatible in the add operator.
I tried to use cast but no use. Can anyone please help me on this
declare #var xml,
#var1 varchar(max),
#var2 varchar(max),
#var3 varchar(max),
#var4 varchar(max),
#var5 varchar(max),
#primary varchar(max);
set #primary='CUSTOMER';
set #var='<RequestData>
<CREATED_BY>nachagon</CREATED_BY>
<CUSTOMER_TYPE />
<modalid>editmodgrid_iBase_VW_Customers</modalid>
<Input_Date_From>31-Dec-2007 07:30:00 PM</Input_Date_From>
<Timestamp>26-Mar-2019 04:02:01 PM</Timestamp>
<UPDATED_ON />
<USER_SELECTED_TIMEZONE>Venezuela Standard Time</USER_SELECTED_TIMEZONE>
<NAME>Kevin Good</NAME>
<CITY>Stewartsville</CITY>
<COUNTRY>US</COUNTRY>
<Input_Date_To>29-Jun-2008 07:30:00 PM</Input_Date_To>
<UPDATED_BY>nachagon</UPDATED_BY>
<CREATED_ON>28-Mar-2019 11:57:46 AM</CREATED_ON>
<CUSTOMER>0000000233</CUSTOMER>
<oper>edit</oper>
<id>jqg1</id>
<tablename>iBase_VW_Customers</tablename>
<moduleId>Customers</moduleId>
<LOGGED_IN_USER_ID>11</LOGGED_IN_USER_ID>
</RequestData>'
select #var1=coalesce(#var1 + ',','')+NodeName , #var2=coalesce(#var2 +',','')+NodeValue
from (select NodeName,NodeValue from(SELECT NodeName = C.value('local-name(.)', 'varchar(50)'),
NodeValue = C.value('(.)[1]', 'varchar(50)') FROM #var.nodes('/RequestData/*') AS T(C))t2 WHERE t2.NodeName NOT IN ('CREATED_BY', 'CREATED_ON', 'id','LOGGED_IN_USER_ID','modalid', 'moduleId','oper','tablename','UPDATED_BY','UPDATED_ON','USER_SELECTED_TIMEZONE'))t
select #var1,#var2
SET #var5= 'select '+#var+'.value(''(RequestData/'+#primary+')[1]'',''varchar(max)'')'
exec (#var5)
print(#var5)
This example might help
declare #primary nvarchar(max) = 'CUSTOMER';
declare #var xml =
'<RequestData>
<CREATED_BY>nachagon</CREATED_BY>
<CUSTOMER_TYPE />
<modalid>editmodgrid_iBase_VW_Customers</modalid>
<Input_Date_From>31-Dec-2007 07:30:00 PM</Input_Date_From>
<Timestamp>26-Mar-2019 04:02:01 PM</Timestamp>
<UPDATED_ON />
<USER_SELECTED_TIMEZONE>Venezuela Standard Time</USER_SELECTED_TIMEZONE>
<NAME>Kevin Good</NAME>
<CITY>Stewartsville</CITY>
<COUNTRY>US</COUNTRY>
<Input_Date_To>29-Jun-2008 07:30:00 PM</Input_Date_To>
<UPDATED_BY>nachagon</UPDATED_BY>
<CREATED_ON>28-Mar-2019 11:57:46 AM</CREATED_ON>
<CUSTOMER>0000000233</CUSTOMER>
<oper>edit</oper>
<id>jqg1</id>
<tablename>iBase_VW_Customers</tablename>
<moduleId>Customers</moduleId>
<LOGGED_IN_USER_ID>11</LOGGED_IN_USER_ID>
</RequestData>'
DECLARE #cmd NVARCHAR(MAX) = 'SELECT #xml.value(''(/RequestData/'+#primary+')[1]'', ''nvarchar(max)'' )'
EXECUTE sp_executesql #cmd, N'#xml XML', #xml = #var
It's not 100% bullet proof but it might help you achieve your goal.
Also, if you know the datatype I recommend to pass this also to the dynamic query.

SQL Server and XML Utilization

I'm trying to utilize XML with SQL Server. All I'm trying to do is print out all three guests. When I run my code, it only shows the prints the first guest's information, and I need all three guest's information to be printed. What am I doing wrong?
SELECT Guest.GuestID, GuestFirst, GuestLast, CheckinDate, Nights
FROM GUEST
JOIN FOLIO
ON Guest.GuestID = Folio.GuestID
FOR XML RAW
Declare #idoc int
Declare #xmldoc nvarchar(4000)
Set #xmldoc = '
<ROOT>
<GUEST>
<GuestID>4431</GuestID>
<GuestFirst>Lacey</GuestFirst>
<GuestLast>Byington</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-02</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>5563</GuestID>
<GuestFirst>Jonathan</GuestFirst>
<GuestLast>Langford</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-05</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>6680</GuestID>
<GuestFirst>Tanner</GuestFirst>
<GuestLast>Olson</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2015-09-11</CheckInDate>
<Nights>3</Nights>
</RESERVATIONDETAIL>
</GUEST>
</ROOT>'
EXEC sp_xml_preparedocument #idoc OUTPUT, #xmldoc
SELECT * FROM OPENXML (#idoc, '/ROOT', 3)
WITH
(
GuestID smallint 'GUEST/GuestID',
GuestFirst varchar(30) 'GUEST/GuestFirst',
GuestLast varchar(30) 'GUEST/GuestLast',
CheckinDate smalldatetime 'GUEST/RESERVATIONDETAIL/CheckInDate',
Nights tinyint 'GUEST/RESERVATIONDETAIL/Nights'
)
EXEC sp_xml_removedocument #idoc
GO
Instead xml document try it with xquery,
DECLARE #xmldoc XML
Set #xmldoc = '
<ROOT>
<GUEST>
<GuestID>4431</GuestID>
<GuestFirst>Lacey</GuestFirst>
<GuestLast>Byington</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-02</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>5563</GuestID>
<GuestFirst>Jonathan</GuestFirst>
<GuestLast>Langford</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-05</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>6680</GuestID>
<GuestFirst>Tanner</GuestFirst>
<GuestLast>Olson</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2015-09-11</CheckInDate>
<Nights>3</Nights>
</RESERVATIONDETAIL>
</GUEST>
</ROOT>'
SELECT
a.b.value('GuestID[1]','smallint') AS GuestID,
a.b.value('GuestFirst[1]','varchar(30)') AS GuestFirst,
a.b.value('GuestLast[1]','varchar(30)') AS GuestLast,
a.b.value('RESERVATIONDETAIL[1]/CheckInDate[1]','smalldatetime') AS CheckInDate,
a.b.value('RESERVATIONDETAIL[1]/Nights[1]','tinyint') AS Nights
FROM #xmldoc.nodes('ROOT/GUEST') AS a(b)
GO
btw, the select query you have given on the top will not produce the same xml which you have given below.
#Jatin answer is good, you can use xquery. You can also use OPENXML like this:
EXEC sp_xml_preparedocument #idoc OUTPUT, #xmldoc
SELECT * FROM OPENXML (#idoc, '/ROOT/GUEST', 3)
WITH
(
GuestID smallint './GuestID',
GuestFirst varchar(30) './GuestFirst',
GuestLast varchar(30) './GuestLast',
CheckinDate smalldatetime './RESERVATIONDETAIL/CheckInDate',
Nights tinyint './RESERVATIONDETAIL/Nights'
)
EXEC sp_xml_removedocument #idoc
You are almost there. You just need to make this small change:
SELECT * FROM OPENXML (#idoc, '/ROOT/*', 3)
WITH
(
GuestID smallint 'GuestID',
GuestFirst varchar(30) 'GuestFirst',
GuestLast varchar(30) 'GuestLast',
CheckinDate smalldatetime 'RESERVATIONDETAIL/CheckInDate',
Nights tinyint 'RESERVATIONDETAIL/Nights'
)

USE Open XML to extract the CDATA

I have xml which has the data embedded in CDATA. I would like to extract the info in different fields. But not able to do it.
<Item_Response Format="text/xml">
<![CDATA[ <Item sequence="1" type="item" itemId="999999"
itemVersion="2012-04-07T13:43:27">
<response><bubbleinput answered="y" input_id="bubbleinput1">
<bubble id="bubble1"/>
</bubbleinput></response></Item> ]]>
</Item_Response>
You can extract CDATA value with XPath. Then use openxml on extracted value.
declare #xml xml = '<Item_Response Format="text/xml"><![CDATA[ <Item sequence="1" type="item" itemId="999999" itemVersion="2012-04-07T13:43:27"><response><bubbleinput answered="y" input_id="bubbleinput1"><bubble id="bubble1"/></bubbleinput></response></Item> ]]></Item_Response>'
-- openxml
declare
#idoc int,
#qxml xml = cast(#xml.value('(/Item_Response)[1]', 'nvarchar(max)') as xml)
exec sp_xml_preparedocument #idoc output, #qxml
select
*
from
openxml(#idoc, '/Item', 0) with (
sequence int '#sequence',
bubbleinput nvarchar(1) './response/bubbleinput/#answered'
) as XMLData
exec sp_xml_removedocument #idoc

SELECT node text values from xml document in TSQL OPENXML

I have a xml document I want to use to update values in a stored procedure. I can process the XML using OPENXML, but I'm confused about extracting the values I want. Each row in the xml is a product record and I want to create a variable for each property. Cell0 is the ID, Cell2 description etc
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<products>
<rows>
<row>
<cell>1</cell>
<cell>BALSAMO DERMOSCENT</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>18.00</cell>
<cell>18.00</cell>
<cell>8.00</cell>
<cell>427</cell>
<cell>No</cell>
</row>
<row>
<cell>2</cell>
<cell>BAYTRIL 150 MG 1 CPDO</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>3.50</cell>
<cell>3.50</cell>
<cell>8.00</cell>
<cell>57</cell>
<cell>No</cell>
</row>
</rows>
</products>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/products/rows/row/cell',1)
with (Col1 varchar(29) 'text()')
Running the above query returns 1 record for each CELL in the xml. I want to be able to return 1 record per row with different columns for each cell, something like:-
Prod Description Qty
---------- -------------------- --------
1 BALSAMO DERMOSCENT 1.00
2 BAYTRIL 150 MG 1 CPDO 1.00
I'm using MSSQL 2008
I've come up with the following which does the job for me
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<products>
<rows>
<row>
<cell>1</cell>
<cell>BALSAMO DERMOSCENT</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>18.00</cell>
<cell>18.00</cell>
<cell>8.00</cell>
<cell>427</cell>
<cell>No</cell>
</row>
<row>
<cell>2</cell>
<cell>BAYTRIL 150 MG 1 CPDO</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>3.50</cell>
<cell>3.50</cell>
<cell>8.00</cell>
<cell>57</cell>
<cell>No</cell>
</row>
</rows>
</products>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/products/rows/row',1)
with (pLineNo int 'cell[1]/text()',
pDesc varchar(50) 'cell[2]/text()',
pQty float 'cell[3]/text()',
pCost float 'cell[4]/text()',
pPvp float 'cell[5]/text()',
pTotal float 'cell[6]/text()',
pIva float 'cell[7]/text()',
pId int 'cell[8]/text()',
pnoFact varchar(5) 'cell[9]/text()')
Why use openxml on sql server 2008?
This is a better option (I used varchar(max) as the datatype, but enter whatever is applicable). Note you have to declare the variable as xml, not varchar.
SELECT
Row.Item.value('data(cell[1])', 'varchar(max)') As Prod,
Row.Item.value('data(cell[2])', 'varchar(max)') As Description,
Row.Item.value('data(cell[3])', 'varchar(max)') As Qty
FROM
#doc.nodes('//row') AS Row(Item)
Note: If you're doing this is a stored procedure you may have to include the following before the select statement:
SET ARITHABORT ON -- required for .nodes
If you must use openxml, at least clean it up when you're done:
exec sp_xml_removedocument #idoc

How to parse xml in sql server to process NULL value in DateTime DataType

I have created a sample query in sql server to parse data from xml and to display it right now.
Although I will be inserting this data in my table but before that I am facing a simple problem.
I want to insert NULL in datetime field ADDED_DATE="NULL" as shown in xml given below. But when I executes this query. It gives me error
Conversion failed when converting datetime from character string.
What mistake am i doing. Please highlight my mistake.
declare #xml varchar(1000)
set #xml= '
<ROOT>
<TX_MAP FK_GUEST_ID="1" FK_CATEGORY_ID="2" ATTRIBUTE="Test" DESCRIPTION="TestDesc" IS_ACTIVE="1" ADDED_BY="NULL" ADDED_DATE="NULL" MODIFIED_BY="NULL" MODIFIED_DATE="NULL"></TX_MAP>
<TX_MAP FK_GUEST_ID="2" FK_CATEGORY_ID="1" ATTRIBUTE="Test2" DESCRIPTION="TestDesc2" IS_ACTIVE="1" ADDED_BY="NULL" ADDED_DATE="NULL" MODIFIED_BY="NULL" MODIFIED_DATE="NULL"></TX_MAP>
</ROOT> '
declare #handle int
exec sp_xml_preparedocument #handle output, #xml
select * from OPENXML(#handle,'/ROOT/TX_MAP',1)
with
(
FK_GUEST_ID INT
,FK_CATEGORY_ID VARCHAR(10)
,ATTRIBUTE VARCHAR(100)
,[DESCRIPTION] VARCHAR(100)
,IS_ACTIVE VARCHAR(10)
,ADDED_BY VARCHAR(100)
,ADDED_DATE DATETIME NULL
,MODIFIED_BY VARCHAR(100)
,MODIFIED_DATE DATETIME NULL
)
I am using Sql Server 2005.
After googling an hour, I got answer to my question and would like to share with you all so that for future users it become easy.
declare #xml varchar(1000)
set #xml= '
<ROOT>
<TX_MAP FK_GUEST_ID="1" FK_CATEGORY_ID="2" ATTRIBUTE="Test" DESCRIPTION="TestDesc" IS_ACTIVE="1" ADDED_BY="NULL" ADDED_DATE="12/3/2010" MODIFIED_BY="NULL" MODIFIED_DATE="12/3/2010"></TX_MAP>
<TX_MAP FK_GUEST_ID="2" FK_CATEGORY_ID="1" ATTRIBUTE="Test2" DESCRIPTION="TestDesc2" IS_ACTIVE="1" ></TX_MAP>
</ROOT> '
declare #handle int
exec sp_xml_preparedocument #handle output, #xml
select * from OPENXML(#handle,'/ROOT/TX_MAP',1)
with
(
FK_GUEST_ID INT
,FK_CATEGORY_ID VARCHAR(10)
,ATTRIBUTE VARCHAR(100)
,[DESCRIPTION] VARCHAR(100)
,IS_ACTIVE VARCHAR(10)
,ADDED_BY VARCHAR(100)
,ADDED_DATE DATETIME
,MODIFIED_BY VARCHAR(100)
,MODIFIED_DATE DATETIME
)
What you need to do is just to omit
those attributes that will result into
NULL value.
An XML element can be set to null like:
<ADDED_DATE xsi:nil="true"/>
I can't find a way to set an attribute to null though. Perhaps the only way is to omit it?

Resources