How to control the output format of XML in SQL - sql

I'm trying to retrieve data from a (SQL Server 2008 R2) table in XML format and can't seem to get it into an ideal structure. I've tried various FOR XML modes and join methods, but I have minimal experience with XML and have not worked with XML output via SQL prior to this.
My data is in a table named XMLResults with the following structure:
ResultID Country Product Disposition Results
-----------------------------------------------
1 UK ABC Result1 100
2 UK ABC Result2 200
3 UK ABC Result3 300
4 UK XYZ Result1 100
5 UK XYZ Result2 200
6 UK XYZ Result3 300
7 USA ABC Result1 100
8 USA ABC Result2 200
9 USA ABC Result3 300
10 USA XYZ Result1 100
11 USA XYZ Result2 200
12 USA XYZ Result3 300
Currently I have this query:
SELECT (SELECT Country,
(SELECT Product,
(SELECT Disposition, Results
FROM XMLResults t1
WHERE t1.ResultID = t2.ResultID
FOR XML PATH ('Dispositions'), TYPE, ELEMENTS
)
FROM XMLResults t2
WHERE t2.ResultID = t3.ResultID
FOR XML PATH ('Products'), TYPE, ELEMENTS
)
FROM XMLResults t3
ORDER BY Country, Product
FOR XML PATH ('Countries'), TYPE, ELEMENTS
)
FOR XML PATH('Stats');
Which returns the XML like this:
<Stats>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result1</Disposition>
<Results>100</Results>
</Dispositions>
</Products>
</Countries>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result2</Disposition>
<Results>200</Results>
</Dispositions>
</Products>
</Countries>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result3</Disposition>
<Results>300</Results>
</Dispositions>
</Products>
</Countries>
...
</Stats>
Not terrible, but I'd prefer it not drop all the way back out to the 'Countries' level after each result, and probably don't need the extra generic tags either.
Something like this would be better:
<Stats>
<Countries>
<Country = "UK">
<Products>
<Product = "ABC">
<Dispositions>
<Disposition>
<ReasonCode>Result1</ReasonCode>
<Count>100</Count>
</Disposition>
<Disposition>
<ReasonCode>Result2</ReasonCode>
<Count>200</Count>
</Disposition>
<Disposition>
<ReasonCode>Result3</ReasonCode>
<Count>300</Count>
</Disposition>
</Dispositions>
</Product>
...
</Products>
</Country>
...
</Countries>
</Stats>
Maybe even something like this (looks a little bit cleaner):
<Stats>
<Country = "UK">
<Product = "ABC">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
<Product = "XYZ">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
</Country>
<Country = "USA">
<Product = "ABC">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
<Product = "XYZ">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
</Country>
</Stats>
The output format isn't set in stone, so if there are recommendations, I'm open to suggestions on that as well.
Thanks,
Sean
Edit for sample data:
CREATE TABLE XMLResults (
ResultID BIGINT IDENTITY(1,1) NOT NULL
, Country VARCHAR(50) NOT NULL
, Product VARCHAR(50) NOT NULL
, Disposition VARCHAR(50) NOT NULL
, Results INT NOT NULL);
INSERT INTO XMLResults (Country, Product, Disposition, Results)
VALUES ('UK', 'ABC', 'Result1', 100)
, ('UK', 'ABC', 'Result2', 200)
, ('UK', 'ABC', 'Result3', 300)
, ('UK', 'XYZ', 'Result1', 100)
, ('UK', 'XYZ', 'Result2', 200)
, ('UK', 'XYZ', 'Result3', 300)
, ('USA', 'ABC', 'Result1', 100)
, ('USA', 'ABC', 'Result2', 200)
, ('USA', 'ABC', 'Result3', 300)
, ('USA', 'XYZ', 'Result1', 100)
, ('USA', 'XYZ', 'Result2', 200)
, ('USA', 'XYZ', 'Result3', 300);

If you want attribute-centric xml, prepend '#' to your column aliases. You can completely customize the output, e.g:
SELECT
Country [#Country],
(SELECT
Product [#Product],
(SELECT
Disposition [#ReasonCode],
Results [#Count]
FROM #XMLResults t3
WHERE t3.Country = t1.Country AND t3.Product = t2.Product
FOR XML PATH('Disposition'),TYPE)
FROM #XMLResults t2
WHERE t2.Country = t1.Country
GROUP BY Product
FOR XML PATH('Product'),TYPE)
FROM #XMLResults t1
GROUP BY Country
FOR XML PATH('Country'), ROOT ('Stats')
Just play around with it.

Related

SQL Query, return data with changes

I would like to isolate only the source key values that changed from '900' to 'RCF'. My code now returns all records regardless of any changes.
select
PD_END_DT,
SOURCE_KEY_VALUE,
Fed_Class_cd
from XXXXXXXXXXXX
where (pd_end_dt = '2015-11-30' or PD_END_DT = '2015-12-31')
and (FED_CLASS_CD = '900' or FED_CLASS_CD = 'RCF')
Sample Data:
pd_ned_dt Source_key_value Fed_Cls_Cd
2015-11-30 CML0100000565101565000000000 900
2015-11-30 CML0100000613059250000613000 900
2015-12-31 CML0100000613059250000613000 RCF
2015-12-31 CML0100000613060250000613000 RCF
2015-11-30 CML0100000613060250000613000 900
2015-11-30 CML0100000613061250000613000 900
2015-12-31 CML0100000613061250000613000 RCF
2015-12-31 CML0100000613062250000613000 RCF
2015-11-30 CML0100000613062250000613000 900
2015-11-30 CML0100000633001633000000000 900
2015-12-31 CML0100000633001633000000000 900
2015-12-31 CML0100000641001641000000000 RCF
2015-11-30 CML0100000641001641000000000 900
2015-11-30 CML0100000641002641000000000 900
2015-12-31 CML0100000641002641000000000 RCF
2015-12-31 CML0100000641003641000000000 RCF
What is a query I can use to display only the records I would like to see? Thanks for the help!
How about this?
SELECT *
FROM #XXXXXXXXXXXX q900
INNER JOIN #XXXXXXXXXXXX qRCF
ON q900.SOURCE_KEY_VALUE = qRCF.SOURCE_KEY_VALUE
AND q900.pd_end_dt < qRCF.pd_end_dt
AND q900.FED_CLASS_CD = '900'
AND qRCF.FED_CLASS_CD = 'RCF'
NB: If PD_END_DT isn't relevant at all, you need some other way of knowing that the RCF record against that Key is newer than the 900 record. Any other fields that could be used for that?
Try this...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NULL
BEGIN -- DROP TABLE #TestData;
CREATE TABLE #TestData (
pd_ned_dt DATE NOT NULL,
Source_key_value CHAR(28) NOT NULL,
Fed_Cls_Cd CHAR(3) NOT NULL
);
INSERT #TestData (pd_ned_dt, Source_key_value, Fed_Cls_Cd) VALUES
('2015-11-30', 'CML0100000565101565000000000', '900'),
('2015-11-30', 'CML0100000613059250000613000', '900'),
('2015-12-31', 'CML0100000613059250000613000', 'RCF'),
('2015-12-31', 'CML0100000613060250000613000', 'RCF'),
('2015-11-30', 'CML0100000613060250000613000', '900'),
('2015-11-30', 'CML0100000613061250000613000', '900'),
('2015-12-31', 'CML0100000613061250000613000', 'RCF'),
('2015-12-31', 'CML0100000613062250000613000', 'RCF'),
('2015-11-30', 'CML0100000613062250000613000', '900'),
('2015-11-30', 'CML0100000633001633000000000', '900'),
('2015-12-31', 'CML0100000633001633000000000', '900'),
('2015-12-31', 'CML0100000641001641000000000', 'RCF'),
('2015-11-30', 'CML0100000641001641000000000', '900'),
('2015-11-30', 'CML0100000641002641000000000', '900'),
('2015-12-31', 'CML0100000641002641000000000', 'RCF'),
('2015-12-31', 'CML0100000641003641000000000', 'RCF');
END;
--=============================================================
SELECT
ChangeDate = td1.pd_ned_dt,
td1.Source_key_value,
BeforeChange = td2.Fed_Cls_Cd,
AfterChange = td1.Fed_Cls_Cd
FROM
#TestData td1
JOIN #TestData td2
ON td1.Source_key_value = td2.Source_key_value
WHERE
td1.Fed_Cls_Cd = 'RCF'
AND td1.pd_ned_dt >= '2015-11-30'
AND td1.pd_ned_dt < '2016-01-01'
AND td2.Fed_Cls_Cd = '900'
AND td1.pd_ned_dt > td2.pd_ned_dt;
Is that what you looking for? I'm not sure if I understood your question..
SELECT
mt2.*
FROM
dbo.MyTable mt1
JOIN dbo.MyTable mt2
ON mt1.SOURCE_KEY_VALUE = mt2.SOURCE_KEY_VALUE
AND mt1.FED_CLASS_CD = 'RCF' // data that changed from RCF
AND mt2.FED_CLASS_CD = '900' //data that changed to 900
AND mt1.PD_END_DT < mt2.PD_END_DT // data that happened after, so updated
WHERE
AND mt1.PD_END_DT >= '2015-11-30'
AND mt1.PD_END_DT < '2016-01-01'
Try The Below query , just an idea you can complete on it. Hope it helps you
;with testD
as
(
select *
, row_number() over(partition by Source_key_value order by pd_ned_dt) rowNumber
from XXXXXXXXXXXX
)
select prevData.*,currentData.*
from testD currentData left join testD prevData on
currentData.Source_key_value= prevData.Source_key_value and currentData.rowNumber=prevData.rowNumber+1
where prevData.Fed_Class_CD ='900'
select distinct pd_ned_dt , Source_key_value , Fed_Cls_Cd from xxx As a
where
Exists(select Source_key_value from xxx where Source_key_value=a.Source_key_value and Fed_Cls_Cd='900')

SQL: Build dynamic query from list of tables received from user

I am attempting to build a dynamic query based on a list of tables as received from a user.
I have a couple attempted solutions below.
I think the CTE is the way to go but am having difficulty figuring out how to make it happen.
I would really appreciate whatever genius can let me know how to do this!
these are the tables:
W, WD, WE, WSF, WSFE, XDF, XDFE, Y, YD, Z, ZD
these are the columns to join by for each group of tables:
W, Y, WD, WE, WSF
WID
WSF, WSFE, XDF
WSFID
XDF, XDFE
XDFID
Y, YD, Z
YID
Z, ZD
ZID
if the user selects W, Y, Z then build this query (which could then be executed by exec or sp_executesql):
select * from #W w join #Y y on y.WID = w.WID join #Z z on z.YID = y.YID
declare #Fields table (
ID int identity not NULL,
Name varchar(200)
)
declare #Tables table (
ID int identity not NULL,
Field varchar(200),
TempTable varchar(200)
)
declare #QueryTables table (
ID int identity not NULL,
[Table] varchar(200),
Alias varchar(20)
)
declare #QueryJoins table (
ID int identity not NULL,
Table1 varchar(20),
Col1 varchar(200),
Table2 varchar(20),
Col2 varchar(200)
)
insert #Fields
values
('W'),
('Y'),
('Z')
insert #Tables
values
('W', '#W'),
('WD', '#WD'),
('WE', '#WE'),
('WSF', '#WSF'),
('WSFE', '#WSFE'),
('XDF', '#XDF'),
('XDFE', '#XDFE'),
('Y', '#Y'),
('YD', '#YD'),
('Z', '#Z'),
('ZD', '#ZD')
insert #QueryTables
values
('#W', 'w'),
('#WD', 'wd'),
('#WE', 'we'),
('#WSF', 'wsf'),
('#WSFE', 'wsfe'),
('#XDF', 'xdf'),
('#XDFE', 'xdfe'),
('#Y', 'y'),
('#YD', 'yd'),
('#Z', 'z'),
('#ZD', 'zd')
insert #QueryJoins
values
('w', 'WID', 'wd', 'WID'),
('w', 'WID', 'we', 'WID'),
('w', 'WID', 'wsf', 'WID'),
('w', 'WID', 'xdf', 'WID'),
('w', 'WID', 'y', 'WID'),
('wd', 'WID', 'w', 'WID'),
('we', 'WID', 'wd', 'WID'),
('wsf', 'WID', 'wd', 'WID'),
('wsf', 'WSFID', 'wsfe', 'WSFID'),
('wsfe', 'WSFID', 'wsf', 'WSFID'),
('wsf', 'WSFID', 'xdf', 'WSFID'),
('xdf', 'WID', 'w', 'WID'),
('xdf', 'WSFID', 'wsf', 'WSFID'),
('xdf', 'XDFID', 'xdfe', 'XDFID'),
('xdfe', 'XDFID', 'xdf', 'XDFID'),
('y', 'WID', 'w', 'WID'),
('y', 'YID', 'yd', 'YID'),
('yd', 'YID', 'y', 'YID'),
('y', 'YID', 'z', 'YID'),
('z', 'YID', 'y', 'YID'),
('z', 'ZID', 'zd', 'ZID'),
('zd', 'ZID', 'z', 'ZID')
--attempted solution number 1:
select
*
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable
join #QueryJoins vqj
on vqj.Table1 = vqt.Alias
join #QueryTables vqt2
on vqt2.Alias = vqj.Table2
join #Tables vt2
on vt2.TempTable = vqt2.[Table]
join #Fields vf2
on vf2.Name = vt2.Field
--attempted solution number 2:
;with cte (FieldID, [Table], Table1, Col1, Table2, Col2, I) as (
select
vf.ID as FieldID,
vqt.[Table],
vqj.Table1,
vqj.Col1,
vqj.Table2,
vqj.Col2,
1
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable
join #QueryJoins vqj
on vqj.Table1 = vqt.Alias
union all
select
vf.ID as FieldID,
vqt.[Table],
vqj.Table1,
vqj.Col1,
vqj.Table2,
vqj.Col2,
I + 1
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable
join #QueryJoins vqj
on vqj.Table1 = vqt.Alias
join cte cte
on cte.Table1 = vqj.Table2
and cte.Table2 = vqj.Table1
where I <= FieldID --a feeble attempt to control the recursion
)
select * from cte
An interesting problem, definitely a data modeling code smell (if Z, Y, and YD all have a YID column, sounds like you need a bridge table or similar construct to manage the relationships there. Or if Z and YD are small lookups, just join them all and let the optimizer handle the overhead.)
Note: I made the fields self join on Name < Name, so you only need the QueryJoins data where Table1 < Table2. This would cut your QueryJoins table in half, but you're also missing some of the "correct" joins (ie you have we/wd but not wd/we)
declare #Fields table (
ID int identity not NULL,
Name varchar(200)
)
declare #Tables table (
ID int identity not NULL,
Field varchar(200),
TempTable varchar(200)
)
declare #QueryTables table (
ID int identity not NULL,
[Table] varchar(200),
Alias varchar(20)
)
declare #QueryJoins table (
ID int identity not NULL,
Table1 varchar(20),
Col1 varchar(200),
Table2 varchar(20),
Col2 varchar(200)
)
insert #Fields
values
('W'),
('Y'),
('Z')
insert #Tables
values
('W', '#W'),
('WD', '#WD'),
('WE', '#WE'),
('WSF', '#WSF'),
('WSFE', '#WSFE'),
('XDF', '#XDF'),
('XDFE', '#XDFE'),
('Y', '#Y'),
('YD', '#YD'),
('Z', '#Z'),
('ZD', '#ZD')
insert #QueryTables
values
('#W', 'w'),
('#WD', 'wd'),
('#WE', 'we'),
('#WSF', 'wsf'),
('#WSFE', 'wsfe'),
('#XDF', 'xdf'),
('#XDFE', 'xdfe'),
('#Y', 'y'),
('#YD', 'yd'),
('#Z', 'z'),
('#ZD', 'zd')
insert #QueryJoins
values
('w', 'WID', 'wd', 'WID'),
('w', 'WID', 'we', 'WID'),
('w', 'WID', 'wsf', 'WID'),
('w', 'WID', 'xdf', 'WID'),
('w', 'WID', 'y', 'WID'),
('wd', 'WID', 'w', 'WID'),
('we', 'WID', 'wd', 'WID'),
('wsf', 'WID', 'wd', 'WID'),
('wsf', 'WSFID', 'wsfe', 'WSFID'),
('wsfe', 'WSFID', 'wsf', 'WSFID'),
('wsf', 'WSFID', 'xdf', 'WSFID'),
('xdf', 'WID', 'w', 'WID'),
('xdf', 'WSFID', 'wsf', 'WSFID'),
('xdf', 'XDFID', 'xdfe', 'XDFID'),
('xdfe', 'XDFID', 'xdf', 'XDFID'),
('y', 'YID', 'yd', 'YID'),
('yd', 'YID', 'y', 'YID'),
('y', 'YID', 'z', 'YID'),
('z', 'ZID', 'zd', 'ZID'),
('zd', 'ZID', 'z', 'ZID')
;
with a as (
select
row_number() over (order by Name) as rn, Name, Field, TempTable, [Table], Alias
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable )
select 'select * from ' + stuff((
select
concat(
case when a.rn =1 then a.TempTable else '' end,
' ',
case when a.rn =1 then a.Alias else '' end,
' join ',
a2.TempTable,
' ' ,
a2.alias,
' on ',
q.Table1,
'.',
q.Col1,
' = ',
q.Table2 ,
'.',
q.Col2 ) from a
left join a a2
on a.name < a2.name
inner join #QueryJoins q
on q.Table1 = a.alias
and q.Table2 = a2.alias
for xml path('')), 1, 1, '')

How to replace the IN operator with EXISTS operator, for the where clause part of the query?

WHERE (
((SERVICECOMPONENT_ID IN (123, 150, 198, 199, 290, 287, 291, 289, 288, 286, 282, 281)))
OR ((SERVICEREQUEST_ID IN (
SELECT distinct(SR.SERVICEREQUEST_ID)
FROM SERVICE_REQUEST SR,ASSIGNED_SR_PROJECTS ASP,PROJECT_RESOURCES PRS
WHERE SR.SERVICEREQUEST_ID = ASP.SERVICEREQUEST_ID
AND PRS.PROJECT_ID = ASP.PROJECT_ID
AND PRS.RESPONSIBILITY IN ('MANAGER','LEAD')
AND PRS.RESOURCE_ID =180 )) )
)
In general,
SELECT a FROM b WHERE c IN (SELECT d FROM e)
is equivalent to
SELECT a FROM b WHERE EXISTS (SELECT 1 FROM e WHERE c = d)
The SERVICEREQUEST_ID IN (subquery) part of your code example translates to:
OR EXISTS (
SELECT 1
FROM
SERVICE_REQUEST SR,
ASSIGNED_SR_PROJECTS ASP,
PROJECT_RESOURCES PRS
WHERE
SR.SERVICEREQUEST_ID = ASP.SERVICEREQUEST_ID
AND PRS.PROJECT_ID = ASP.PROJECT_ID
AND PRS.RESPONSIBILITY IN ('MANAGER', 'LEAD')
AND PRS.RESOURCE_ID = 180
AND mytable.SERVICEREQUEST_ID = SR.SERVICEREQUEST_ID
)
if you have static list of elements It's better to use "IN".... If you have a subquery and it is returning more than one value then Use Exist...
There is no Difference in Both clauses..
WHERE (
( (SERVICECOMPONENT_ID IN ( 123 , 150 , 198 , 199 , 290 , 287 , 291 , 289 , 288 , 286 , 282 , 281 )) )
OR ( (SERVICEREQUEST_ID IN ( 1952 , 2387 , 3618 , 3633 , 4178 , 4432 , 5090 , 5271 , 6068 , 6320 , 6396 , 6526 , 7162 , 7442 , 7558 , 7639 , 7688 , 8176 , 8189 , 8338 , 8460 , 8461 , 8598 , 8612 , 8628 , 8675 , 8775 , 8869 , 8886 , 8898 )) )
OR ( (REQUESTED_BY LIKE 'XXXXXXX#example.com' ) )
OR ( ( EXISTS ( SELECT count(distinct(SR.SERVICEREQUEST_ID)) FROM SERVICE_REQUEST SR,ASSIGNED_SR_PROJECTS ASP,PROJECT_RESOURCES PRS WHERE SR.SERVICEREQUEST_ID = ASP.SERVICEREQUEST_ID AND PRS.PROJECT_ID = ASP.PROJECT_ID AND PRS.RESPONSIBILITY IN ('MANAGER','LEAD') AND PRS.RESOURCE_ID =180 )) )
OR ( (STATUS_CODE LIKE 'OPEN' ) AND ( EXISTS (SELECT count(COMPONENT.CATEGORY_ID) FROM PROJECTMASTER PROJECTS, BUDGET BUDGET, CONTRACT CONTRACT,COMPONENTS COMPONENT, PROJECT_RESOURCES PROJ_RESOURCES, CATEGORY_OWNER_ASSIGNMENT CATEGORYOWNER, SERVICECATEGORYASSIGNMENT CATEGORYASSIGNMENT WHERE PROJECTS.PROJECT_ID = PROJ_RESOURCES.PROJECT_ID AND PROJECTS.BUDGET_ID = BUDGET.BUDGET_ID AND BUDGET.CONTRACT = CONTRACT.CONTRACT_ID AND CATEGORYASSIGNMENT.CONTRACT_ID = CONTRACT.CONTRACT_ID AND COMPONENT.COMPONENT_ID = CATEGORYASSIGNMENT.COMPONENT_ID AND CATEGORYOWNER.CATEGORY_ID = COMPONENT.CATEGORY_ID AND CATEGORYOWNER.USER_ID = PROJ_RESOURCES.RESOURCE_ID AND (CATEGORYOWNER.OWNER_FLAG = 'Y' OR CATEGORYOWNER.MEMBER_FLAG = 'Y') AND PROJ_RESOURCES.RESOURCE_ID = 180 AND PROJ_RESOURCES.ACTIVE_FLAG = 'Y' AND CATEGORYASSIGNMENT.ACTIVE_FLAG = 'Y' AND PROJ_RESOURCES.RESPONSIBILITY IN ('MANAGER', 'LEAD') )) )
)
read this for further clarification..
Difference between EXISTS and IN in SQL?

How can I save data from xml to sql 2008?

How can I save data from xml to sql 2008?
SQL table:
[dbo].[Position](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ImoNo] [numeric](8, 0) NOT NULL,
[sid] [numeric](5, 0) NULL,
[VesselName] [nvarchar](20) NULL,
[time] [datetime] NOT NULL,
[lat] [numeric](9, 2) NULL,
[lon] [numeric](9, 2) NULL,
[sog] [numeric](9, 2) NULL,
[cog] [numeric](9, 2) NULL,
[hdg] [numeric](9, 2) NULL,
[eta] [datetime] NULL,
[NextPort] [nvarchar](20) NULL)
XML file:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.fleettracker.de/api/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:GetPositionsResponse>
<body>
<result>Found 2 vessels.</result>
<success>true</success>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9456159</ns1:imono>
<ns1:sid>780</ns1:sid>
<ns1:name>Trenta</ns1:name>
<ns1:charterShipName>Trenta</ns1:charterShipName>
<ns1:pasttrack>
<lat>1832900</lat>
<lon>7570400</lon>
<timestamp>2014-01-14T08:28:45Z</timestamp>
<orderNumber>0</orderNumber>
<sog>9.5</sog>
<cog>22</cog>
<hdg>22</hdg>
<eta>2014-01-15T12:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1872560</lat>
<lon>7589000</lon>
<timestamp>2014-01-14T07:00:00Z</timestamp>
<orderNumber>1</orderNumber>
<sog>10.8</sog>
<cog>25</cog>
<hdg>25</hdg>
</ns1:pasttrack>
</shipsWithPositions>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9144055</ns1:imono>
<ns1:sid>789</ns1:sid>
<ns1:name>Vipava</ns1:name>
<ns1:charterShipName>Vipava</ns1:charterShipName>
<ns1:pasttrack>
<lat>1757160</lat>
<lon>7536240</lon>
<timestamp>2014-01-13T19:00:00Z</timestamp>
<orderNumber>2</orderNumber>
<sog>9.4</sog>
<cog>21</cog>
<hdg>21</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1658200</lat>
<lon>7476480</lon>
<timestamp>2014-01-13T07:00:00Z</timestamp>
<orderNumber>3</orderNumber>
<sog>8.4</sog>
<cog>29</cog>
<hdg>29</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1630000</lat>
<lon>7455400</lon>
<timestamp>2014-01-13T03:00:03Z</timestamp>
<orderNumber>4</orderNumber>
<sog>8.83</sog>
<cog>34</cog>
<hdg>34</hdg>
<eta>2014-01-15T08:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
</shipsWithPositions>
</body>
</ns1:GetPositionsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I tried with this query, but I get error msg
'XQuery [nodes()]: The names "SOAP-ENV" and "ns1:" do not denote a namespace.'
DECLARE #xml XML
DECLARE #character VARCHAR(MAX)
SELECT #character = x.y
FROM OPENROWSET( BULK 'C:\Users\Nale\Desktop\POS.xml', SINGLE_CLOB ) x(y)
-- Fix up the ampersand
SELECT #xml = REPLACE( #character, '&', '&' )
-- Get the tally information
SELECT
x.y.value('ns1:imono/text())[1]', 'NUMERIC (8,0)') ImoNo,
x.y.value('ns1:sid/text())[1]', 'NUMERIC (5,0)') sid,
x.y.value('ns1:VesselName/text())[1]', 'NVARCHAR (20)') VesselName,
x.y.value('ns1:pasttrack/time/text())[1]', 'DATETIME') time,
x.y.value('ns1:pasttrack/lat/text())[1]', 'NUMERIC (9,2)') lat,
x.y.value('ns1:pasttrack/lon/text())[1]', 'NUMERIC (9,2)') lon,
x.y.value('ns1:pasttrack/sog/text())[1]', 'NUMERIC (9,2)') sog,
x.y.value('ns1:pasttrack/cog/text())[1]', 'NUMERIC (9,2)') cog,
x.y.value('ns1:pasttrack/hdg/text())[1]', 'NUMERIC (9,2)') hdg,
x.y.value('ns1:pasttrack/eta/text())[1]', 'DATETIME') eta,
x.y.value('ns1:pasttrack/NextPort/text())[1]', 'NVARCHAR (20)') NextPort
FROM #xml.nodes('SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:GetPositionsResponse/body/shipsWithPositions') AS x(y)
XML file is on local disk.
Will I use some sql query, or what is the best way to save data from xml to sql table?
Make XML valid
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.fleettracker.de/api/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:GetPositionsResponse>
<body>
<result>Found 2 vessels.</result>
<success>true</success>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9456159</ns1:imono>
<ns1:sid>780</ns1:sid>
<ns1:name>Trenta</ns1:name>
<ns1:charterShipName>Trenta</ns1:charterShipName>
<ns1:pasttrack>
<lat>1832900</lat>
<lon>7570400</lon>
<timestamp>2014-01-14T08:28:45Z</timestamp>
<orderNumber>0</orderNumber>
<sog>9.5</sog>
<cog>22</cog>
<hdg>22</hdg>
<eta>2014-01-15T12:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1872560</lat>
<lon>7589000</lon>
<timestamp>2014-01-14T07:00:00Z</timestamp>
<orderNumber>1</orderNumber>
<sog>10.8</sog>
<cog>25</cog>
<hdg>25</hdg>
</ns1:pasttrack>
</shipsWithPositions>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9144055</ns1:imono>
<ns1:sid>789</ns1:sid>
<ns1:name>Vipava</ns1:name>
<ns1:charterShipName>Vipava</ns1:charterShipName>
<ns1:pasttrack>
<lat>1757160</lat>
<lon>7536240</lon>
<timestamp>2014-01-13T19:00:00Z</timestamp>
<orderNumber>2</orderNumber>
<sog>9.4</sog>
<cog>21</cog>
<hdg>21</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1658200</lat>
<lon>7476480</lon>
<timestamp>2014-01-13T07:00:00Z</timestamp>
<orderNumber>3</orderNumber>
<sog>8.4</sog>
<cog>29</cog>
<hdg>29</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1630000</lat>
<lon>7455400</lon>
<timestamp>2014-01-13T03:00:03Z</timestamp>
<orderNumber>4</orderNumber>
<sog>8.83</sog>
<cog>34</cog>
<hdg>34</hdg>
<eta>2014-01-15T08:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
</shipsWithPositions>
</body>
</ns1:GetPositionsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
2. Add namespaces to your query
DECLARE #xml XML
DECLARE #character VARCHAR(MAX)
SELECT #xml = x.y
FROM OPENROWSET( BULK 'C:\Users\Nale\Desktop\POS.xml', SINGLE_CLOB ) x(y)
-- Get the tally information
;WITH XMLNAMESPACES (
'http://www.fleettracker.de/api/1.0' as ns1,
'http://www.w3.org/2001/XMLSchema-instance' AS xsi,
'http://schemas.xmlsoap.org/soap/envelope/' AS e
)
SELECT
x.y.value('(ns1:imono/text())[1]', 'NUMERIC (8,0)') ImoNo,
x.y.value('(ns1:sid/text())[1]', 'NUMERIC (5,0)') sid,
x.y.value('(ns1:VesselName/text())[1]', 'NVARCHAR (20)') VesselName,
x.y.value('(ns1:pasttrack/time/text())[1]', 'DATETIME') time,
x.y.value('(ns1:pasttrack/lat/text())[1]', 'NUMERIC (9,2)') lat,
x.y.value('(ns1:pasttrack/lon/text())[1]', 'NUMERIC (9,2)') lon,
x.y.value('(ns1:pasttrack/sog/text())[1]', 'NUMERIC (9,2)') sog,
x.y.value('(ns1:pasttrack/cog/text())[1]', 'NUMERIC (9,2)') cog,
x.y.value('(ns1:pasttrack/hdg/text())[1]', 'NUMERIC (9,2)') hdg,
x.y.value('(ns1:pasttrack/eta/text())[1]', 'DATETIME') eta,
x.y.value('(ns1:pasttrack/NextPort/text())[1]', 'NVARCHAR (20)') NextPort
FROM #xml.nodes('e:Envelope/e:Body/ns1:GetPositionsResponse/body/shipsWithPositions') AS x(y)

Get all the Subchild values using XQUERY-SQL

I'm attempting to create an xquery expression that will return all the subchild node values. Now I get only the first subchild under each child.
I have the following XML:
<order>
<child1>
<subchild id="S1">
<name type="primary">
<first>NAOMI</first>
<middle />
<last>ADAMS</last>
<suffix />
</name>
</subchild>
<subchild id="S2">
<name type="primary">
<first>TOVER</first>
<middle />
<last>DALI</last>
<suffix />
</name>
</subchild>
</child1>
<child2>
<subchild id="V1">
<year>2002</year>
<make>PONTI</make>
<model>AZTEK</model>
<vin>3G7DA03E32S597676</vin>
</subchild>
<subchild id="V2">
<year>2003</year>
<make>HONDA</make>
<model>CIVIC</model>
<vin>2H94DA03E80S1538</vin>
</subchild>
</child2>
<child3>
<subchild id="A1">
<house>7741</house>
<street1>SAINT BERNARD ST</street1>
<apartment />
<city>PLAYA DEL REY</city>
<state>CA</state>
<postalcode>90293</postalcode>
</subchild>
<subchild id="A2">
<house>2371</house>
<street1>HANNUM DR</street1>
<apartment />
<city>MARINA DEL REY</city>
<state>CA</state>
<postalcode>90293</postalcode>
</subchild>
</child3>
</order>
My result should look like :
FirstName LastName HouseNumber StreetName City SState Zip Year Model Make VIN
NAOMI ADAMS 7741 SAINT BERNARD ST PLAYA DEL REY CA 90293 2002 PONTI AZTEK G7DA03E32S597676
TOVER DALI 2371 HANNUM DR MARINA DEL REY CA 90024 2003 HONDA CIVIC 2H94DA03E80S1538
My query :
SELECT
FirstName, LastName, HouseNumber, StreetName, City, SState, Zip, Year, Model, Make, VIN
FROM
xmlTable
OUTER APPLY
(
SELECT
tbl.col.value('(child1/subchild/name/first)[1]','varchar(20)') AS FirstName,
tbl.col.value('(child1/subchild/name/last)[1]','varchar(20)') AS LastName,
tbl.col.value('(child1/subchild/name/middle)[1]','varchar(20)') AS MiddleName,
tbl.col.value('(child3/subchild/house)[1]','varchar(20)') AS HouseNumber,
tbl.col.value('(child3/subchild/street1)[1]','varchar(20)') AS StreetName,
tbl.col.value('(child3/subchild/city)[1]','varchar(20)') AS City,
tbl.col.value('(child3/subchild/state)[1]','varchar(20)') AS SState,
tbl.col.value('(child3/subchild/postalcode)[1]','varchar(20)') AS Zip,
tbl.col.value('(child2/subchild/model_year)[1]','varchar(20)') AS Year,
tbl.col.value('(child2/subchild/model)[1]','varchar(20)') AS Model,
tbl.col.value('(child2/subchild/make)[1]','varchar(20)') AS Make,
tbl.col.value('(child2/subchild/vin)[1]','varchar(20)') AS VIN
FROM
xmldocument.nodes('//order') AS tbl(col)
) Y
Thanks
DECLARE #x XML
SELECT #x = 'Your XML here'
SELECT
tbl.col.value('(child1/subchild/name/first)[position() = sql:column("s.number")][1]','varchar(20)') AS FirstName,
tbl.col.value('(child1/subchild/name/last)[position() = sql:column("s.number")][1]', 'varchar(20)') AS LastName,
tbl.col.value('(child1/subchild/name/middle)[position() = sql:column("s.number")][1]', 'varchar(20)') AS MiddleName,
tbl.col.value('(child3/subchild/house)[position() = sql:column("s.number")][1]', 'varchar(20)') AS HouseNumber,
tbl.col.value('(child3/subchild/street1)[position() = sql:column("s.number")][1]', 'varchar(20)') AS StreetName,
tbl.col.value('(child3/subchild/city)[position() = sql:column("s.number")][1]', 'varchar(20)') AS City,
tbl.col.value('(child3/subchild/state)[position() = sql:column("s.number")][1]', 'varchar(20)') AS SState,
tbl.col.value('(child3/subchild/postalcode)[position() = sql:column("s.number")][1]', varchar(20)') AS Zip,
tbl.col.value('(child2/subchild/model_year)[position() = sql:column("s.number")][1]', 'varchar(20)') AS Year,
tbl.col.value('(child2/subchild/model)[position() = sql:column("s.number")][1]', 'varchar(20)') AS Model,
tbl.col.value('(child2/subchild/make)[position() = sql:column("s.number")][1]', 'varchar(20)') AS Make,
tbl.col.value('(child2/subchild/vin)[position() = sql:column("s.number")][1]', 'varchar(20)') AS VIN
FROM #x.nodes('/order') AS tbl(col)
CROSS JOIN master..spt_values s
WHERE type = 'P' AND number BETWEEN 1
AND col.query('count(child1/subchild)').value('.', 'INT')