Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 
        
Home       Members    Calendar    Who's On


Add to briefcase

FOR XML EXPLICIT - One to many relationship at level 3 (3 = parent, 4 = child) Expand / Collapse
Author
Message
Posted Monday, July 20, 2009 4:19 PM
SSC-Enthusiastic

SSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-Enthusiastic

Group: General Forum Members
Last Login: 2 days ago @ 9:21 AM
Points: 119, Visits: 666
I am trying to generate a one time export to send to a vendor. They are requesting XML and for one of the items, the spec indicates "One parent tag – multiple child tags". I'm really close, but am struggling with this particular parent/child level. I need to use FOR XML EXPLICIT because they want CDATA tags on certain fields.

I received a small sample file and am on the right track, but the sample file did not include an example of this particular parent/child section so I believe this is the expected end result (based on the "One parent tag - multiple child tags" reference). NOTE: My problem area is "Languages".

Expected Result:
<Requisitions>
<Record>
<EmployeeID>101</EmployeeID>
<FirstName>Jane</FirstName>
<LastName>Doe</LastName>
<JobTitle><![CDATA[Accountant - Level I]]></JobTitle>
<Languages>
<language>English</language>
</Languages>
</Record>
<Record>
<EmployeeID>102</EmployeeID>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<JobTitle><![CDATA[Business Analyst]]></JobTitle>
<Languages>
<language>English</language>
<language>Spanish</language>
</Languages>
</Record>
<Record>
<EmployeeID>103</EmployeeID>
<FirstName>Erin</FirstName>
<LastName>Thopmson</LastName>
<JobTitle><![CDATA[Database Administrator]]></JobTitle>
<Languages>
<language>French</language>
<language>Greek</language>
<language>Italian</language>
</Languages>
</Record>
</Requisitions>

I've created some sample tables to help illustrate my problem with my SQL statement at the end:
-- Create table variables
declare @Employee table
(EmployeeID int NOT NULL,
FirstName varchar(30) NOT NULL,
LastName varchar(30) NOT NULL,
JobTitle varchar(50) NOT NULL)
--
declare @Language table
(LanguageID int NOT NULL,
LanguageName varchar(30) NOT NULL)
--
declare @EmployeeLanguage table
(EmployeeID int NOT NULL,
LanguageID int NOT NULL)
--
-- Populate table variables with sample data
insert into @Language values (1, 'English')
insert into @Language values (2, 'Spanish')
insert into @Language values (3, 'French')
insert into @Language values (4, 'Italian')
insert into @Language values (5, 'Greek')
--
insert into @Employee values (101, 'Jane', 'Doe', 'Accountant - Level I')
insert into @Employee values (102, 'John', 'Smith', 'Business Analyst')
insert into @Employee values (103, 'Erin', 'Thopmson', 'Database Administrator')
--
insert into @EmployeeLanguage values (101, 1)
insert into @EmployeeLanguage values (102, 1)
insert into @EmployeeLanguage values (102, 2)
insert into @EmployeeLanguage values (103, 3)
insert into @EmployeeLanguage values (103, 4)
insert into @EmployeeLanguage values (103, 5)
--
-- Render as XML
select 1 as Tag,
NULL as Parent,
NULL as 'Requisitions!1',
NULL as 'Record!2!EmployeeID!element',
NULL as 'Record!2!FirstName!element',
NULL as 'Record!2!LastName!element',
NULL as 'Record!2!JobTitle!cdata',
NULL as 'Languages!3!language!element'
union
SELECT 2 as Tag,
1 as Parent,
NULL,
EmployeeID,
FirstName,
LastName,
JobTitle,
NULL
FROM @Employee
union all
select 3 as Tag,
2 as Parent,
NULL,
e.EmployeeID,
e.FirstName,
e.LastName,
e.JobTitle,
l.LanguageName
from @Employee e
inner join @EmployeeLanguage el on e.EmployeeID = el.EmployeeID
inner join @Language l on el.LanguageID = l.LanguageID
order by 'Record!2!EmployeeID!element', 'Languages!3!language!element'
for xml explicit

But this is rendering individual Language nodes for each child language. For example, for Erin Thompson:

  <Record>
<EmployeeID>103</EmployeeID>
<FirstName>Erin</FirstName>
<LastName>Thopmson</LastName>
<JobTitle><![CDATA[Database Administrator]]></JobTitle>
<Languages>
<language>French</language>
</Languages>
<Languages>
<language>Greek</language>
</Languages>
<Languages>
<language>Italian</language>
</Languages>
</Record>

I've looked at several articles trying to figure out how to do this and I just can't get Languages to render correctly. It's almost making me wonder: Is what I'm trying to do not possible?? Or am I missing something?

Thank you for any guidance -
Lisa
Post #756230
Posted Monday, July 20, 2009 6:47 PM
Ten Centuries

Ten CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen Centuries

Group: General Forum Members
Last Login: Wednesday, October 24, 2012 2:12 PM
Points: 1,213, Visits: 3,232
Try this FOR XML EXPLICIT query.

SELECT 1 AS Tag,
0 AS Parent,
NULL AS [REQUISITIONS!1],
NULL AS [RECORD!2!EMPLOYEEID!element],
NULL AS [RECORD!2!FIRSTNAME!element],
NULL AS [RECORD!2!LASTNAME!element],
NULL AS [RECORD!2!JOBTITLE!cdata],
NULL AS [LANGUAGES!3],
NULL AS [LANGUAGE!4]
UNION ALL
SELECT 2 AS Tag,
1 AS Parent,
NULL,
EmployeeID,
FirstName,
LastName,
JobTitle,
NULL,
NULL
FROM @Employee
UNION ALL
SELECT 3 AS Tag,
2 AS Parent,
NULL,
EmployeeID,
NULL,
NULL,
NULL,
NULL,
NULL
FROM @Employee
UNION ALL
SELECT 4 AS Tag,
3 as Parent,
NULL,
EL.EmployeeID,
NULL,
NULL,
NULL,
NULL,
L.LanguageName
FROM @EmployeeLanguage EL
INNER JOIN @Language L ON (EL.LanguageID = L.LanguageID)
ORDER BY [RECORD!2!EMPLOYEEID!element], [LANGUAGE!4]
FOR XML EXPLICIT

However, I would question why you really must have CDATA sections. The FOR XML PATH statement will properly encode XML character entities (e.g. '&' as '&amp;', '<' as '&lt;' and '>' as '&gt;') and any decent XML parser should see no difference between encoded XML character entities and the equivalent unencoded characters in a CDATA section. Here is an alternative query that uses FOR XML PATH.

SELECT 
E.EmployeeID AS EMPLOYEEID,
E.FirstName AS FIRSTNAME,
E.LastName AS LASTNAME,
E.JobTitle AS JOBTITLE,
(
SELECT L.LanguageName AS '*'
FROM @Language L
INNER JOIN @EmployeeLanguage EL ON (L.LanguageID = EL.LanguageID)
WHERE (EL.EmployeeID = E.EmployeeID)
FOR XML PATH('LANGUAGE'), ROOT('LANGUAGES'), Type
)
FROM @Employee E
FOR XML PATH('RECORD'), ROOT('REQUISITIONS')

Post #756283
Posted Tuesday, July 21, 2009 8:38 AM
SSC-Enthusiastic

SSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-EnthusiasticSSC-Enthusiastic

Group: General Forum Members
Last Login: 2 days ago @ 9:21 AM
Points: 119, Visits: 666
Thank you VERY much! I had tried adding a fourth union and had definitely been doing it wrong. And I year you on the CDATA portion. Here is what the spec said:

"For XML files we recommend the use of CDATA Tags in all fields, but minimally in the nText fields. Otherwise the escaped values of the reserved XML characters will be the actual text entered into the database. For example, & l t ; b r & g t ; will be entered in the database instead of just < b r >."

(Had to throw a lot of spaces in there to get the text to display properly.)

For whatever it's worth, this is from ADP. We are getting rid of a third party recruiting system and will be utilizing something ADP offers and therefore need to get that legacy data into the new system.

Appreciate the help! This is the best technical forum!!

Lisa
Post #756648
« Prev Topic | Next Topic »

Add to briefcase

Permissions Expand / Collapse