Select into xml from table in sql server with distinct id - sql-server

I have table that contains three columns:
[ID],[Name],[Value]
My select for xml result:
DECLARE #xml XML
SET #xml = (
SELECT
ID
,[Name]
,[Value]
FROM [CustomerDetails]
WHERE ID = 1
FOR XML PATH(''), ROOT('Customer')
)
SELECT #xml
My select return xml with multiple ID properties:
<Customer>
<ID>1</ID>
<Name>FirstName</Name>
<Value>firstName</Value>
<ID>1</ID>
<Name>LastName</Name>
<Value>lastName</Value>
<ID>1</ID>
<Name>Age</Name>
<Value>20</Value>
<ID>1</ID>
<Name>City</Name>
<Value>London</Value>
</Customer>
I need next xml:
<Customer>
<ID>1</ID>
<Name>FirstName</Name>
<Value>firstName</Value>
<Name>LastName</Name>
<Value>lastName</Value>
<Name>Age</Name>
<Value>20</Value>
<Name>City</Name>
<Value>London</Value>
</Customer>
How to return this kind of XML?

I have shortened name of columns:
declare #id int = 1
select id, n, v from
(select #id id, null n, null v, 1 as rn from t
union
select null, n, v, 2 as rn from t
where id = #id
) t order by rn
for xml path(''), root('customer')
Output:
<customer><id>1</id><n>n1</n><v>v1</v><n>n2</n><v>v2</v><n>n3</n><v>v3</v></customer>
Fiddle http://sqlfiddle.com/#!3/70ea0/4

Related

How can I use with inside apply in sql server? I know another solution without apply. but can we do with outer apply? if 'yes' then how?

Can we use with inside any apply in the SQL server?
For traversing all nodes in the XML file I am trying to use outer apply so I can traverse in one go.
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT CustomerID, CustomerName, Address
FROM OPENXML(#hDoc, 'ROOT/Customers/Customer')
WITH
(
CustomerID [varchar](50) '#CustomerID',
CustomerName [varchar](100) '#CustomerName',
Address [varchar](100) 'Address'
)
OUTER APPLY
(SELECT OrderDate, OrderID--, Address
FROM OPENXML(#hDoc, 'ROOT/Customers/Customer/Orders/Order')
WITH
(
OrderDate [varchar](100) '#OrderDate',
OrderID [varchar](100) '#OrderID'
Address [varchar](100) 'Address'
) as Orders
OUTER APPLY
(SELECT Quantity, ProductID--, Address
FROM OPENXML(#hDoc, 'ROOT/Customers/Customer/Orders/Order/OrderDetail')
WITH
(
Quantity [varchar](100) '#Quantity',
ProductID [varchar](100) '#ProductID'
Address [varchar](100) 'Address'
) as OrderDetail
this is the XML file
<ROOT>
<Customers>
--root/customers/cusomer/orders/order/OrderDetail
<Customer CustomerName="Arshad Ali" CustomerID="C001">
<Orders>
<Order OrderDate="2012-07-04T00:00:00" OrderID="10248">
<OrderDetail Quantity="5" ProductID="10"/>
<OrderDetail Quantity="12" ProductID="11"/>
<OrderDetail Quantity="10" ProductID="42"/>
</Order>
</Orders>
<Address> Address line 1, 2, 3</Address>
</Customer>
</Customers>
</ROOT>
and this is how i want my result.
CustomerID
CustomerName
Address
OrderID
OrderDate
ProductID
Quantity
C001
Arshad Ali
Address line 1, 2, 3
10248
2012-07-04 00:00:00.000
10
5
C001
Arshad Ali
Address line 1, 2, 3
10248
2012-07-04 00:00:00.000
11
12
C001
Arshad Ali
Address line 1, 2, 3
10248
2012-07-04 00:00:00.000
42
10
I know another solution without apply. but can we do with outer apply? if 'yes' then how?
I am new with this so please help me.
To do this, you can choose to go the deepest level and then "crawl up" from it.
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = N'<ROOT>
<Customers>
--root/customers/cusomer/orders/order/OrderDetail
<Customer CustomerName="Arshad Ali" CustomerID="C001">
<Orders>
<Order OrderDate="2012-07-04T00:00:00" OrderID="10248">
<OrderDetail Quantity="5" ProductID="10"/>
<OrderDetail Quantity="12" ProductID="11"/>
<OrderDetail Quantity="10" ProductID="42"/>
</Order>
</Orders>
<Address> Address line 1, 2, 3</Address>
</Customer>
</Customers>
</ROOT>'
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
select *
FROM OPENXML(#hDoc, 'ROOT/Customers/Customer/Orders/Order/OrderDetail')
WITH
(
CustomerID NVARCHAR(30) '../../../../Customer/#CustomerID'
, CustomerName NVARCHAR(100) '../../../../Customer/#CustomerName'
, Address NVARCHAR(100) '../../../Address'
, OrderID NVARCHAR(30) '../#OrderID'
, OrderDate DATETIME '../#OrderDate'
, ProductID INT '#ProductID'
, Quantity INT '#Quantity'
)
exec sp_xml_removedocument #hDoc OUTPUT -- Always clean up your XMLs!
Edit #2: "real" apply version:
DECLARE #XML AS XML
SELECT #XML = N'<ROOT>
<Customers>
--root/customers/cusomer/orders/order/OrderDetail
<Customer CustomerName="Arshad Ali" CustomerID="C001">
<Orders>
<Order OrderDate="2012-07-04T00:00:00" OrderID="10248">
<OrderDetail Quantity="5" ProductID="10"/>
<OrderDetail Quantity="12" ProductID="11"/>
<OrderDetail Quantity="10" ProductID="42"/>
</Order>
</Orders>
<Address> Address line 1, 2, 3</Address>
</Customer>
</Customers>
</ROOT>'
SELECT cu.value('#CustomerName', 'nvarchar(1000)') name
, cu.value('#CustomerID', 'nvarchar(100)') id
, cu.value('Address[1]', 'NVARCHAR(1000)') address
, oo.value('#OrderDate', 'datetime') orderdate
, oo.value('#OrderID', 'varchar(30)') orderid
, ood.value('#Quantity', 'int') qty
, ood.value('#ProductID', 'varchar(10)') productId
FROM (
SELECT #xml AS x
) xml
OUTER APPLY x.nodes('ROOT/Customers/Customer') c(cu)
OUTER APPLY cu.nodes('Orders/Order') o(oo)
OUTER APPLY oo.nodes('OrderDetail') od(ood)

SQL Server XQuery - Find the original data type

In the following code, I want to check if column is a Number. If it is - fill it with leading zeros.
Is there anyway using XML XQuery to check the original data type (int)
of the column?
declare #T table (string nchar(10), id int)
insert #T
select 'test1', 1
insert #T
select 'test2', 2
declare #X xml
SET ANSI_WARNINGS ON
set #X = (select * from #T order by id for xml path('row'), root('root'))
SELECT (
STUFF(
(
SELECT ';' + v.value('.','nvarchar(max)')
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x.nodes('/root/row') AS A(r)
As you know your table you should not use a generical approach, if you do not have a good reason to do so.
What you probably should do is this
Include the formatted value into your XML. This allows you, to carry both information within the structure: typed and formatted.
declare #T table (string nvarchar(10), id int)
insert #T values
('test1', 1)
,('test2', 22)
declare #x xml = (select string
,REPLACE(STR(id,8),' ','0') AS [id/#formatted]
,id
from #T
order by id for xml path('row'), root('root'),TYPE)
SELECT (
STUFF(
(
SELECT ';' + r.value('(string/text())[1]','nvarchar(max)')
+ ';' + r.value('(id/#formatted)[1]','nvarchar(max)')
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x.nodes('/root/row') AS A(r);
If you need the dynamic approach - look at this
declare #T table (string nchar(10), id int)
insert #T values
('test1', 1)
,('test2', 2)
declare #X xml = (select * from #T order by id for xml path('row'), root('root'))
select #x;
The first thing you see, that you - probably want to use NVARCHAR(10) instead of NCHAR(10). You might use LTRIM() too:
<root>
<row>
<string>test1 </string>
<id>1</id>
</row>
<row>
<string>test2 </string>
<id>2</id>
</row>
</root>
Now I start from scratch with NVARCHAR(10)
declare #T2 table (string nvarchar(10), id int);
insert #T2 values
('test1', 1)
,('test2', 22);
declare #X2 xml = (select * from #T2 order by id for xml path('row'), root('root'));
--First try is with ISNUMERIC and CASE WHEN
SELECT (
STUFF(
(
SELECT ';' + CASE WHEN ISNUMERIC(v.value('.','nvarchar(max)'))=1
THEN REPLACE(STR(v.value('.','int'),8),' ','0')
ELSE v.value('.','nvarchar(max)') END
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x2.nodes('/root/row') AS A(r);
But: There are some character formats (scientific notations), which can be taken as numeric incidentically.
Add this to your table and try again
,('3d2',333) --breaks, because SELECT ISNUMERIC('3d2'),ISNUMERIC('1e1') returns 1 (for both)
Other/better approaches
If you are using SQL-Server 2012 or higher, you can use TRY_CAST, which will return NULL instead of an error
SELECT (
STUFF(
(
SELECT ';' + CASE WHEN TRY_CAST(v.value('.','nvarchar(max)') AS INT) IS NOT NULL
THEN REPLACE(STR(v.value('.','int'),8),' ','0')
ELSE v.value('.','nvarchar(max)') END
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x2.nodes('/root/row') AS A(r);
Another option might the the explicit XQuery cast (Find possible XQuery functions here)
SELECT (
STUFF(
(
SELECT ';' + v.query('let $nd:=string(./text()[1])
,$nr:=concat("00000000",string($nd cast as xs:int?))
return if(string-length($nr)=8)
then $nd
else substring($nr,string-length($nr)-7)').value('.','nvarchar(max)')
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x2.nodes('/root/row') AS A(r);
UPDATE
XML is not aware of an underlying type unless you specify a schema. Look at this:
declare #T2 table (string nvarchar(10), id int)
insert #T2 values
('test1', 1)
,('test2', 22)
,('1',333)
declare #x2 xml = (select * from #T2 order by id for xml raw('row'), root('root'),xmlschema)
select #x2;
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="http://schemas.microsoft.com/sqlserver/2004/sqltypes" targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" elementFormDefault="qualified">
<xsd:import namespace="http://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="http://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
<xsd:element name="row">
<xsd:complexType>
<xsd:attribute name="string">
<xsd:simpleType>
<xsd:restriction base="sqltypes:nvarchar" sqltypes:localeId="1033" sqltypes:sqlCompareOptions="IgnoreCase IgnoreKanaType IgnoreWidth">
<xsd:maxLength value="10" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="id" type="sqltypes:int" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" string="test1" id="1" />
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" string="test2" id="22" />
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" string="1" id="333" />
</root>
In this case you might ask the schema for the underlying type. But - if I get you correctly - you want to look at any value if it might be a number. This works as shown, but is extremely dangerous...

Create Xml with Same tag name

Table Name : sample
Column Name : id,name
Every Row Create Separate tag with inside.
Show the Xml value like this
<Details>
<id>1</id>
<name>na</name>
<Details>
<id>2</id>
<name>aa</name>
</Details>
</Details>
I tried like this but its not working
select
id 'Details\id'
,name 'Details\name'
from sample
How do get that xml output?
It is hardcoded but should work:
DECLARE #x xml
SELECT #x = (
SELECT x+''
FROM (
SELECT '%details?%id?'+CAST(id as nvarchar(max))+'%/id?%name?'+name+'%/name?' x
FROM [sample] s
UNION ALL
SELECT '%/details?'
FROM [sample] s
) as t
FOR XML PATH('')
)
SELECT CAST(REPLACE(REPLACE((CAST(#x as nvarchar(max))),'%','<'),'?','>') as xml)
In [sample] table I got:
(1,'na'),
(2,'aa'),
(3,'sd')
Output:
<details>
<id>1</id>
<name>na</name>
<details>
<id>2</id>
<name>aa</name>
<details>
<id>3</id>
<name>sd</name>
</details>
</details>
</details>
EDIT
Also it could be done with recursive CTE:
DECLARE #x xml
;WITH rec AS (
SELECT CAST((
SELECT TOP 1 id,
[name]
FROM [sample]
ORDER BY id DESC
FOR XML PATH('details')
) as xml) as d,
1 as [Level]
UNION ALL
SELECT CAST((
SELECT id,
[name],
cast(r.d as xml)
FROM [sample]
WHERE s.id = id
FOR XML PATH('details')
) as xml) as d,
r.[Level]+1
FROM [sample] s
INNER JOIN rec r
ON s.id = CAST(r.d.query('/details/id/text()') as nvarchar(max))-1
)
SELECT TOP 1 WITH TIES d
FROM rec
ORDER BY [Level] desc
Same output.
You can use query like this:
SELECT
*,
(SELECT
*
FROM #details
WHERE id = 2
FOR xml PATH ('Details'), TYPE)
FROM #details
WHERE id = 1
FOR xml PATH ('Details')
For inner loop you can use CTE
Table creation scripts :
CREATE TABLE #details (
id int,
name varchar(10)
)
INSERT INTO #details (id, name)
VALUES (1, 'test'), (2, 'test2')

Create single XML from SQL Server multiple tables query

I would like to query multiple tables from SQL Server 2005 and create a single XML document and do this in a stored procedure.
I know that I can query multiple tables within a stored procedure and get a DataSet in my .NET application that can be easily saved as XML. However, I'm trying to do something similar within the context of a stored procedure.
Essentially I want to do something like this:
declare #x xml
select #x = x.result
from (select y.* from tabley y for xml path('y')
union
select a.* from tablea a for xml path('aa')
) as x
select #x
If you want them just one after the other, you can try something like this:
SELECT
(SELECT y.* FROM dbo.TableY FOR XML PATH('y'), TYPE) AS 'YElements',
(SELECT a.* FROM dbo.TableA FOR XML PATH('aa'), TYPE) AS 'AElements'
FOR XML PATH(''), ROOT('root')
That return an XML something like:
<root>
<YElements>
<Y>
....
</Y>
<Y>
....
</Y>
......
</YElements>
<AElements>
<A>
....
</A>
<A>
....
</A>
......
</AElements>
</root>
SELECT -- Root Starts
(SELECT '1' AS ErrorCode FOR XML PATH(''), TYPE) AS 'Results', -- Level 1 Starts
(select -- Level 2 Starts
(select '1' CustomerID, -- Level 2 Detail Starts
'John' CustomerName,
'Doe' CustomerLastname,
'Y' Active
for xml path('Customers'), type) AS 'Customer' -- Level 2 Detail Ends
for xml path(''), TYPE) AS 'Response' -- Level 2 Ends
FOR XML PATH('') -- Level 1 Ends
,ROOT('BaseXML') -- Root Ends
Convert Table XML in sql server.
Declare #RESULTXML XML
Declare #SMS_REGISTER TABLE([id] VARCHAR(30),[status] VARCHAR(30))
Declare #EMAIL_REGISTER TABLE([id] VARCHAR(30),[status] VARCHAR(30))
Declare #ODP_REGISTER TABLE([id] VARCHAR(30),[status] VARCHAR(30))
Select #RESULTXML =(
SELECT (SELECT * FROM #SMS_REGISTER FOR XML PATH('sms'), TYPE) AS 'smss',
(SELECT * FROM #EMAIL_REGISTER FOR XML PATH('email'), TYPE) AS 'emails',
(SELECT * FROM #ODP_REGISTER FOR XML PATH('odp'), TYPE) AS 'odps'
FOR XML PATH('subroot'), ROOT('root') )
Return XML Like this
<root>
<subroot>
<smss>
<sms>
<id>NT0000000020</id>
<status>registered</status>
</sms>
<sms>
<id>NT0000000021</id>
<status>registered</status>
</sms>
<sms>
<id>NT0000000022</id>
<status>registered</status>
</sms>
<sms>
<id>NT0000000023</id>
<status>registered</status>
</sms>
</smss>
<emails>
<email>
<id>NT0000000024</id>
<status>registered</status>
</email>
<email>
<id>NT0000000025</id>
<status>registered</status>
</email>
</emails>
</subroot>
</root>

How to get parent element name using OpenXML in sqlserver

If I have some xml:
<Users>
<User>
<property1>sdfd</property1>
...
<User>
...
</Users>
And my sql is:
SELECT
*
FROM
OpenXML(#idoc, '/Users/User')
WITH (
[property1] varchar(50) 'property1',
...
)
How can I get the name of the parent element and return that in the dataset?
This page from MSDN docs on OpenXML seems to indicate you should be able to use the ".." notation for the parent:
declare #idoc int
declare #doc varchar(1000)
set #doc ='<ROOT>
<Customer CustomerID="VINET" ContactName="Paul Henriot">
<Order OrderID="10248" CustomerID="VINET" EmployeeID="5"
OrderDate="1996-07-04T00:00:00">
<OrderDetail ProductID="11" Quantity="12"/>
<OrderDetail ProductID="42" Quantity="10"/>
</Order>
</Customer>
<Customer CustomerID="LILAS" ContactName="Carlos Gonzlez">
<Order OrderID="10283" CustomerID="LILAS" EmployeeID="3"
OrderDate="1996-08-16T00:00:00">
<OrderDetail ProductID="72" Quantity="3"/>
</Order>
</Customer>
</ROOT>'
--Create an internal representation of the XML document.
exec sp_xml_preparedocument #idoc OUTPUT, #doc
-- SELECT stmt using OPENXML rowset provider
SELECT *
FROM OPENXML (#idoc, '/ROOT/Customer/Order/OrderDetail',2)
WITH (OrderID int '../#OrderID',
CustomerID varchar(10) '../#CustomerID',
OrderDate datetime '../#OrderDate',
ProdID int '#ProductID',
Qty int '#Quantity')
Does that work in your case, too?
UPDATE:
Try this (the "pseudo-attribute" #mp:parentlocalname) :
SELECT *
FROM OPENXML (#idoc, '/ROOT/Customer/Order/OrderDetail',2)
WITH (OrderID int '../#OrderID',
CustomerID varchar(10) '../#CustomerID',
OrderDate datetime '../#OrderDate',
ProdID int '#ProductID',
Qty int '#Quantity',
ParentNodeName varchar(50) '#mp:parentlocalname' )
Does this now do what you want? :-)
See a whole list of these "pseudo-attributes" in this article at ExtremeExperts.
Marc

Resources