Adding XML Schema Reference in SQL - sql

I can't find for the life of me if it's possible to add a direct reference to the schema in the root of my XML.
Here is my code:
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' )
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
Which produces the following results:
<Employees>
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2016-10-18T16:09:48.110</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee>
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>
Is there a way to add part in my SQL to make the root tag to include the following:
<Employees xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
Thank you for the help!

Always had trouble with xmlns, so I use a little hack
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' )
Declare #XML xml = (
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
)
set #XML.modify('insert ( attribute MyNameSpace {"https://www.blahblah.org/blahblah/BlahSchema.xsd"}) into (/Employees)[1]')
set #XML = replace(cast(#XML as nvarchar(max)),'[MyNameSpace]','xmlns')
Select #XML
Returns
<Employees xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2017-01-10T15:19:42.873</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee>
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>
EDIT to Exclude Empty Values
NULL will be omitted by default, so if you want to exclude empty values (not null), You can convert the value to NULL via NullIf(). For example
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' ),
(3,'Betty',null ), -- No element for Last_Name
(4,'Susan','' ) -- No element for Last_Name
Declare #XML xml = (
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select EmployeeID = NullIf(EmployeeID,'')
,First_Name = NullIf(First_Name,'')
,Last_Name = NullIf(Last_Name,'')
From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
)
set #XML.modify('insert ( attribute MyNameSpace {"https://www.blahblah.org/blahblah/BlahSchema.xsd"}) into (/Employees)[1]')
set #XML = replace(cast(#XML as nvarchar(max)),'[MyNameSpace]','xmlns')
Select #XML

You can use the WITH NAMESPACES keyword, but that adds the name space to all the nodes:
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' );
WITH XMLNAMESPACES (DEFAULT 'https://www.blahblah.org/blahblah/BlahSchema.xsd')
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
Result:
<Employees xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<RecordHeader xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2017-01-10T16:36:23.450</CreateDate>
</RecordHeader>
<Employee xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>

Related

FOR XML SQL Query to generate a Record Header

I am trying to generate a XML using FOR XML SQL Query. Structure of the XML is pretty straight forward, except the requirement calls for an extra sub-element, which has to appear only once at the top of the document between the Root Element and record element. Here is an example:
<Employees xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>1900-01-01T01:01:01-06:00</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>4</EmployeeID>
<FirstName>Rob</FirstName>
<LastName>Walters</LastName>
</Employee>
<Employee>
<EmployeeID>168</EmployeeID>
<FirstName>Rob</FirstName>
<LastName>Caron</LastName>
</Employee>
</Employees>
I created a FOR XML Query but I can't seem to get it to work properly for the RecordHeader. Any suggestions? Thanks!
SELECT
(SELECT 'My Organization' as [CreatedBy],
CONVERT(VARCHAR(33), GetDate(), 126) as [CreateDate] FOR XML PATH('RecordHeader'), ELEMENTS),
EmployeeID, FirstName, LastName
FROM Employees
FOR XML PATH('Employee'), ROOT('Employees'), ELEMENTS
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' )
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
Returns
<Employees>
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2016-10-18T16:09:48.110</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee>
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>

Generate Empty xml root node in SQL even if there are no records

I have a problem with generating empty\null xml root node from sql. This is an exmaple of the structure that I'm working on:
<Departments>
<Department>
<Employees>
<Employee>
<Name></Name>
<ID></ID>
</Employee>
</Employees>
</Department>
</Departments>
Below is the sql that I use to generate the structure :
declare #Employee table (Name varchar(50), ID int, DID int)
declare #Department table (Name varchar(50), DID int)
declare #xmldata xml
insert into #Employee values ('AAA', 1, 3)
insert into #Employee values ('BBB', 2, 3)
--insert into #Department values ('CCC', 3)
SET #xmldata =
(
select GETDATE() as 'ReportDate', (
select D.Name,
(
select (
select E.Name,
E.ID
from #Employee E
where E.DID = D.DID
for xml path ('Employee'), elements xsinil, type
)
for xml path('Employees'), type
)
from #Department D
for xml path('Department'), type
)
for xml path ('Departments'), type
)
select #xmldata
If there are records, the xml structure is coming properly but my problem is if there are no department record, there is no node at all. How do I show at least the empty node in the xml?
If no department record, xml will be :
<Departments>
<ReportDate>2016-08-11T16:31:22.960</ReportDate>
</Departments>
What I like to have is :
<Departments>
<ReportDate>2016-08-11T16:31:22.960</ReportDate>
<Department />
</Departments>
Use a derived table with 1 row and do an outer apply to a query against #Departments.
select getdate() as ReportDate,
(
select D.Name,
isnull(D.Employees, '') as Employees
from (select null) as T(N)
outer apply (
select D.Name,
(
select E.Name,
E.ID
from #Employee E
where E.DID = D.DID
for xml path ('Employee'), elements xsinil, type
) as Employees
from #Department as D
) as D
for xml path('Departments'), type
)
for xml path('Departments'), type
I got to know about your issue. You may try below concept.
SELECT ISNULL(s.lmx, '<Users></Users>')
FROM
(
select 1 as tag, null as parent,
FirstName as [User!1!FirstName!Element],
LastName as [User!1!LastName!Element]
FROM Users
FOR XML EXPLICIT
) AS s(lmx)
I too faced the same problem and I got the hint from here

Add XML attribute in mssql using stored procedure

i want output
<CustomerName>
<Customer id='1'>
<Name>xyz</Name>
</Customer>
<Customer id='2'>
<Name>Abc</Name>
</Customer>
</CustomerName>
for this output i wrote SP
SELECT
'' AS [CustomerName],
(SELECT
Name[Customer/Name]
FOR XML PATH(''), TYPE) AS[CustomerName/Customer]
FOR XML PATH('')
not able to add Id attribute, please help me
Try this:
DECLARE #tblCust TABLE(id INT, CustomerName VARCHAR(100));
INSERT INTO #tblCust VALUES
(1,'xyz')
,(2,'Abc');
SELECT id AS [#id]
,CustomerName AS [Name]
FROM #tblCust
FOR XML PATH('Customer'),ROOT('CustomerName')
This is the result
<CustomerName>
<Customer id="1">
<Name>xyz</Name>
</Customer>
<Customer id="2">
<Name>Abc</Name>
</Customer>
</CustomerName>
SELECT [#id] = t.id, [Name] = t.name
FROM (
VALUES (1, 'name'), (2, 'name2')
) t (id, name)
FOR XML PATH('Customer'), ROOT('CustomerName')

SQL Server - Replace text in XML variable based on join

For my situation, I have similar XML to the following:
DECLARE #MyXML XML
SET #MyXML =
'
<Students>
<Student>
<Nickname>Cat</Nickname>
<Name>Catherine</Name>
</Student>
<Student>
<Nickname>Cat</Nickname>
<Name>Joseph</Name>
</Student>
</Students>
'
SELECT T.Students.value('Nickname[1]', 'varchar(20)')
FROM #MyXML.nodes('/Students/Student[Nickname = "Cat"]') T(Students)
JOIN dbo.MyStudents CLASS ON T.Students.value('Name[1]', 'varchar(20)') = CLASS.StudentName
I want to update the records that get returned from this SELECT statement, replacing the Nickname with CLEARED. So, if dbo.MyStudents has Joseph but not Catherine, the resulting #MyXML variable would be:
<Students>
<Student>
<Nickname>Cat</Nickname>
<Name>Catherine</Name>
</Student>
<Student>
<Nickname>CLEARED</Nickname>
<Name>Joseph</Name>
</Student>
</Students>
Is there a way to do this by modifying the XML variable directly? Or will I need to put the XML variable into a temp table and perform a .modify on the temp table?
So far, examples that I have found do not have the conditional update with .modify based on the join.
Give this example a try. For more info see Syntax of the FOR XML Clause.
-- sample #MyStudents table
declare #MyStudents table (StudentName varchar(90), NickName varchar(90));
insert #MyStudents values('Kent', '');
insert #MyStudents values('Robert', 'Bobby');
insert #MyStudents values('Lisa', 'LB');
insert #MyStudents values('Venkat', 'Big Cat');
insert #MyStudents values('Catherine', 'Cat');
insert #MyStudents values('Joseph', 'Joey');
DECLARE #MyXML XML
SET #MyXML =
'<Students>
<Student>
<Nickname>Cat</Nickname>
<Name>Catherine</Name>
</Student>
<Student>
<Nickname>Joey</Nickname>
<Name>Joseph</Name>
</Student>
<Student>
<Nickname>Bonzo</Nickname>
<Name>Clifford</Name>
</Student>
</Students>'
select #MyXML = (
select (
select * from (
-- set existing #MyStudent records to CLEARED
select
'CLEARED' as NickName,
T.Students.value('Name[1]', 'varchar(20)') as Name
from #MyXML.nodes('/Students/Student') T(Students)
join #MyStudents CLASS ON T.Students.value('Name[1]', 'varchar(20)') = CLASS.StudentName
union
-- get students that aren't in #MyStudents table
select
T.Students.value('Nickname[1]', 'varchar(20)') as NickName,
T.Students.value('Name[1]', 'varchar(20)') as Name
from #MyXML.nodes('/Students/Student') T(Students)
where not exists(select 1 from #MyStudents s where s.StudentName = T.Students.value('Name[1]', 'varchar(20)'))
) as t1
for xml path('Student'), type)
for xml path('Students'), type)

How to insert xml into a node in another xml using XQuery?

I have two xml variable say #res, #student in a stored proc in SQL server 2005.
#res contains
<Subject>English</Subject>
<Marks>67</Marks>
<Subject>Science</Subject>
<Marks>75</Marks>
#student contains:
<Student>
<Name>XYZ</Name>
<Roll>15</Roll>
<Result />
<Attendance>50</Attendance>
</Student>
I need to insert the xml of #res into the node Result in #student variable using XQuery.
How to implement that?
Please help.
In SQL Server 2008, it's pretty easy:
DECLARE #res XML = '<Subject>English</Subject>
<Marks>67</Marks>
<Subject>Science</Subject>
<Marks>75</Marks>'
DECLARE #student XML = '<Student>
<Name>XYZ</Name>
<Roll>15</Roll>
<Result />
<Attendance>50</Attendance>
</Student>'
SET #student.modify('insert sql:variable("#res") as first into (/Student/Result)[1]')
SELECT #student
That gives me the output:
<Student>
<Name>XYZ</Name>
<Roll>15</Roll>
<Result>
<Subject>English</Subject>
<Marks>67</Marks>
<Subject>Science</Subject>
<Marks>75</Marks>
</Result>
<Attendance>50</Attendance>
</Student>
Unfortunately, the ability to call .modify() and use a sql:variable in the insert statement was introduced with SQL Server 2008 only - doesn't work in SQL Server 2005.
I don't see how you could do this in SQL Server 2005, other than resorting back to ugly string parsing and replacement:
SET #student =
CAST(REPLACE(CAST(#student AS VARCHAR(MAX)),
'<Result/>',
'<Result>' + CAST(#res AS VARCHAR(MAX)) + '</Result>') AS XML)
Marc
This will work in SQL 2005 and is mostly an xquery solution:
DECLARE #res xml
SET #res =
'<Subject>English</Subject>
<Marks>67</Marks>
<Subject>Science</Subject>
<Marks>75</Marks>'
DECLARE #student xml
SET #student =
'<Student>
<Name>XYZ</Name>
<Roll>15</Roll>
<Result />
<Attendance>50</Attendance>
</Student>'
DECLARE #final XML
SET #final = CAST(CAST(#student AS VARCHAR(MAX)) + '<test>' + CAST(#res AS VARCHAR(MAX)) + '</test>' AS XML)
SET #final.modify('insert /test/* into (/Student/Result)[1]')
SET #final.modify('delete /test')
SELECT #final
You can set your #student variable to #final at that point if you need to do that. The name of "test" for the node was just what I chose to use. You can use any name as long as it will not already appear in your XML.
You basically just throw the two XML strings together so that they are both available to xquery at once.
You may also try to go back to relational data and than back to xml; something like:
DECLARE #res xml =
'<result>
<StudentID>1</StudentID>
<Subject>English</Subject>
<Marks>67</Marks>
</result>
<result>
<StudentID>1</StudentID>
<Subject>Science</Subject>
<Marks>75</Marks>
</result>'
DECLARE #student xml =
'<Student>
<StudentID>1</StudentID>
<Name>XYZ</Name>
<Roll>15</Roll>
<Attendance>50</Attendance>
</Student>'
;
WITH cte_1
AS ( SELECT t.c.value('StudentID[1]', 'int') AS [StudentID]
,t.c.value('Subject[1]', 'varchar(50)') AS [Subject]
,t.c.value('Marks[1]', 'int') AS [Marks]
FROM #res.nodes('/result') AS t ( c )
),
cte_2
AS ( SELECT t.c.value('StudentID[1]', 'int') AS [StudentID]
,t.c.value('Name[1]', 'varchar(50)') AS [Name]
,t.c.value('Roll[1]', 'int') AS [Roll]
,t.c.value('Attendance[1]', 'int') AS [Attendance]
FROM #student.nodes('/Student') AS t ( c )
)
SELECT student.StudentID
,student.[Name]
,student.Roll
,student.Attendance
,( SELECT result.[Subject]
,result.Marks
FROM cte_1 AS result
WHERE student.StudentID = result.StudentID
FOR
XML AUTO
,TYPE
,ELEMENTS
)
FROM cte_2 AS student
FOR XML AUTO
,ELEMENTS
Returns:
<student>
<StudentID>1</StudentID>
<Name>XYZ</Name>
<Roll>15</Roll>
<Attendance>50</Attendance>
<result>
<Subject>English</Subject>
<Marks>67</Marks>
</result>
<result>
<Subject>Science</Subject>
<Marks>75</Marks>
</result>
</student>
Not exactly your example, but close.