I need to produce XML out of my table in the format like below. Can you advise what method I should go with (Path, Element, Raw) or could it be a combination of several?
Please find below my test data and some code I just started. Also see the requested XML format with a lot of nests. Can this be done in a single SELECT
or can I compose various parts by level?
SELECT * INTO #t FROM (
SELECT 2020 yy, 'Alpha' CompanyName,
'BossA' FirstName_Owner, 'Smith' LastName_Owner, 'John1' FirstName_Client , 'Dow1' LasttName_Client,'Yes' Active union
SELECT 2020 yy, 'Alpha' CompanyName,
'BossA' FirstName_Owner, 'Smith' LastName_Owner, 'Peter2' FirstName_Client , 'Redd2' LasttName_Client,'No' Active UNION
SELECT 2020 yy, 'Alpha' CompanyName,
'BossyBB' FirstName_Owner, 'Green' LastName_Owner, 'Mary' FirstName_Client , 'Robbins3' LasttName_Client,'Yes' Active
)a --- SELECT TOP 100 * FROM #t -- DROP TABLE IF EXISTS #t
SELECT yy, CompanyName
--SELECT
-- FirstName_Owner, LastName_Owner,
-- FirstName_Client , LasttName_Client, Active
FROM #t
FOR XML RAW('CompanyInfo') , ELEMENTS -- Path
--FOR XML RAW('Client'), ROOT ('Clients')
Requested output in XML format for my test data:
<GemReport>
<Version>2.0</Version>
<Company>
<CompanyInfo>
<Year>2020</Year>
<CompanyName>Alpha</CompanyName>
<Owners>
<Owner>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
<Clients>
<Client>
<FirstName>John1</FirstName>
<LastName>Doe1</LastName>
<Contract>
<Active>Yes</Active>
</Contract>
</Client>
<Client>
<FirstName>Peter2</FirstName>
<LastName>Redd2</LastName>
<Contract>
<Active>NO</Active>
</Contract>
</Client>
</Clients>
</Owner>
<Owner>
<FirstName>BossyBB</FirstName>
<LastName>Green</LastName>
<Clients>
<Client>
<FirstName>Mary</FirstName>
<LastName>Robbins3</LastName>
<Contract>
<Active>Yes</Active>
</Contract>
</Client>
</Owners>
</CompanyInfo>
</Company>
</GemReport>
As your design is denormalised, you're going to need to use some self JOIN
s here using CTEs to the DISTINCT
values of the company and company owner details. Then you can nest your FOR XML PATH
s for each layer (Owners
, and Clients
) with subqueries.
Most likely getting a performant solution, with a larger dataset, will actually be to fix your design and normalise it; then you don't need to scan the same table multiple times, and instead can have a table for the companies, owners, and clients, each with relevant primary/foreign key constraints and indexes.
USE Sandbox;
GO
SELECT *
INTO dbo.YourTable
FROM (SELECT 2020 AS yy,
'Alpha' AS CompanyName,
'BossA' AS FirstName_Owner,
'Smith' AS LastName_Owner,
'John1' AS FirstName_Client,
'Dow1' AS LasttName_Client,
'Yes' AS Active
UNION --ALL?
SELECT 2020 AS yy,
'Alpha' AS CompanyName,
'BossA' AS FirstName_Owner,
'Smith' AS LastName_Owner,
'Peter2' AS FirstName_Client,
'Redd2' AS LasttName_Client,
'No' AS Active
UNION --ALL?
SELECT 2020 AS yy,
'Alpha' AS CompanyName,
'BossyBB' AS FirstName_Owner,
'Green' AS LastName_Owner,
'Mary' AS FirstName_Client,
'Robbins3' AS LasttName_Client,
'Yes' AS Active) a;
GO
WITH Companies AS(
SELECT DISTINCT
CompanyName,
yy
FROM dbo.YourTable),
CompanyOwners AS(
SELECT DISTINCT
CompanyName,
yy,
FirstName_Owner,
LastName_Owner
FROM dbo.YourTable)
SELECT 2.0 AS Version,
(SELECT C.yy AS [Year],
C.CompanyName AS [CompanyName],
(SELECT CO.FirstName_Owner AS FirstName,
CO.LastName_Owner AS LastName,
(SELECT YT.FirstName_Client AS FirstName,
YT.LasttName_Client AS LastName,
YT.Active AS [Contract/Active]
FROM dbo.YourTable YT
WHERE YT.yy = CO.yy
AND YT.CompanyName = CO.CompanyName
AND YT.FirstName_Owner = CO.FirstName_Owner --I hope this can't be NULL
AND YT.LastName_Owner = CO.LastName_Owner --I hope this can't be NULL
FOR XML PATH('Client'),ROOT('Clients'),TYPE)
FROM CompanyOwners CO
WHERE CO.yy = C.yy
AND CO.CompanyName = C.CompanyName
FOR XML PATH('Owner'),ROOT('Owners'),TYPE)
FROM Companies C
FOR XML PATH('CompanyInfo'), ROOT('Company'),TYPE)
FOR XML PATH('GemReport')
GO
DROP TABLE dbo.YourTable;
Which produces the following XML:
<GemReport>
<Version>2.0</Version>
<Company>
<CompanyInfo>
<Year>2020</Year>
<CompanyName>Alpha</CompanyName>
<Owners>
<Owner>
<FirstName>BossA</FirstName>
<LastName>Smith</LastName>
<Clients>
<Client>
<FirstName>John1</FirstName>
<LastName>Dow1</LastName>
<Contract>
<Active>Yes</Active>
</Contract>
</Client>
<Client>
<FirstName>Peter2</FirstName>
<LastName>Redd2</LastName>
<Contract>
<Active>No</Active>
</Contract>
</Client>
</Clients>
</Owner>
<Owner>
<FirstName>BossyBB</FirstName>
<LastName>Green</LastName>
<Clients>
<Client>
<FirstName>Mary</FirstName>
<LastName>Robbins3</LastName>
<Contract>
<Active>Yes</Active>
</Contract>
</Client>
</Clients>
</Owner>
</Owners>
</CompanyInfo>
</Company>
</GemReport>
Note, this does not match the OP's required XML, as that is not valid XML. The last Clients
and Owner
nodes are not closed. I assumed this was in error.