Using SQL to Generate XML - sql

I'm trying to use SQL to generate XML in the format:
<ImportSession>
<Batches>
<Batch>
<BatchFields>
<BatchField Name="Field1" Value="1" />
<BatchField Name="Field2" Value="2" />
<BatchField Name="Field3" Value="3" />
</BatchFields>
<Batch>
<Batches>
</ImportSession>
I'm using SQL Server 2008. I wrote this query:
SELECT
(SELECT
(SELECT
'Col' AS [#Name],
FiscalYear AS [#Value]
FROM [ICEM].[dbo].[ExportedBill]
WHERE ExportedBillID = 1
FOR XML PATH ('BatchField'), TYPE)
FROM [ICEM].[dbo].[ExportedBill]
WHERE ExportedBillID = 1
FOR XML PATH ('BatchFields'), ROOT ('Batch'), TYPE)
FROM
[ICEM].[dbo].[ExportedBill]
WHERE
ExportedBillID = 1
FOR XML PATH ('Batches'), ROOT ('ImportSession')
And this results in:
<ImportSession>
<Batches>
<Batch>
<BatchFields>
<BatchField Name="Col" Value="2015" />
</BatchFields>
</Batch>
</Batches>
</ImportSession>
What I need though is every column should have an entry in BatchField. Also I need the column name to show up in the name. So I should get:
<BatchField Name="FiscalYear" Value="2015" />
<BatchField Name="MeterNumber" Value="123456" />
<BatchField Name="Name" Value="John Smith" />
<BatchField Name="Utility" Value="Electricity" />
So can anyone tell me how I modify my query to get what I need?
EDIT:
I figured it out. I needed a second nested Select. I need one for each column. If they proceeding selects use the same tags as a previous Select then the information is concatanated under the same parent tag
SELECT
(SELECT
(SELECT
'FiscalYear' AS [#Name],
FiscalYear AS [#Value]
FROM [ICEM].[dbo].[ExportedBill]
WHERE ExportedBillID = 1
FOR XML PATH ('BatchField'), TYPE),
(SELECT 'FiscalPeriod' AS [#Name],
FiscalPeriod AS [#Value]
FROM [PEEL_ICEM].[dbo].[ExportedBill]
WHERE ExportedBillID = 1
FOR XML PATH ('BatchField'), TYPE)
FROM [ICEM].[dbo].[ExportedBill]
WHERE ExportedBillID = 1
FOR XML PATH ('BatchFields'), ROOT ('Batch'), TYPE)
FROM
[ICEM].[dbo].[ExportedBill]
WHERE
ExportedBillID = 1
FOR XML PATH ('Batches'), ROOT ('ImportSession')
Thing is though, there will be around 70 columns in this table. Ill brute force it for now, but if anyone knows of a better way to do this please let me know. Cheers

You can create separate child elements by adding a blank column separator. e.g.
DECLARE #T TABLE
( FiscalYear INT,
MeterNumber INT,
Name VARCHAR(255),
Utility VARCHAR(255)
);
INSERT #T VALUES (2015, 123456, 'John Smith', 'Electricity');
SELECT [BatchField/#Name] = 'FiscalYear',
[BatchField/#Value] = FiscalYear,
'',
[BatchField/#Name] = 'MeterNumber',
[BatchField/#Value] = MeterNumber,
'',
[BatchField/#Name] = 'Name',
[BatchField/#Value] = Name,
'',
[BatchField/#Name] = 'Utility',
[BatchField/#Value] = Utility
FROM #T
FOR XML PATH('BatchFields'), ROOT('Batch');
Which gives:
<Batch>
<BatchFields>
<BatchField Name="FiscalYear" Value="2015" />
<BatchField Name="MeterNumber" Value="123456" />
<BatchField Name="Name" Value="John Smith" />
<BatchField Name="Utility" Value="Electricity" />
</BatchFields>
</Batch>

Related

Split XML report in SQL

I have below SQL stored procedure which generates a report and sends to me via email.
However it sends all the various rows as one big chunk report.
Rather I would want it to split each error
Any idea how I can split the below query to have individual XML reports generated.
DECLARE #Report XML
BEGIN
UPDATE [Orders].dbo.PurOrd
SET [Status] = 'Failed', Reason = '<e id="0" message="failed test order" />'
WHERE [Status] = 'InProcess'
SET #Report = (
SELECT
p.Name as "#Name",
p.Customer "#Customer",
CASE p.Name
WHEN 'Default' THEN convert(xml,p.RejectedReason)
ELSE convert(xml,f.RejectedReason)
END AS "RejectedReason",
(
SELECT u.first_name as "#FirstName",
u.last_name as "#LastName",
FROM [Users].dbo.Users u
WHERE u.user_id = u.user_id
for xml PATH('Users'), type
),
(
SELECT
li.Product as "#PId",
li.Quantity as "#Quantity",
li.SalePrice as "#Price",
FROM [Cart].dbo.LineItems li
WHERE li.OrderFormId = f.OrderFormId
ORDER BY li.ItemNumber
for xml PATH('LineItem'), type
)
FROM [Orders].dbo.OrForms f
JOIN [Orders].dbo.PurOrd p on f.GroupId = p.GroupId
WHERE
(p.Status = 'OrderRejected' AND p.ReportStatus IS NULL)
FOR XML PATH('test'), TYPE
)
SELECT #Report FOR XML PATH('Report')
Current Output:
<Report>
<PurchaseOrder OrderId="Order 1" Name="name1" Seller="abc">
<Reason>
<errors>
<e id="0" message="failed test ord" />
</errors>
</Reason>
<Users FirstName="abc" LastName="xyz"/>
<LineItem ProductId="Clothes1" Quantity="1.0000" SalePrice="100rs"/>
</PurchaseOrder>
<PurchaseOrder OrderId="Order 2" Name="name 2" Seller="abc">
<Reason>
<errors>
<e id="0" message="failed test ord" />
</errors>
</Reason>
<Users FirstName="abc" LastName="xyz"/>
<LineItem ProductId="Clothes1" Quantity="1.0000" SalePrice="100rs"/>
</PurchaseOrder>
</Report>
EXPECTED OUTPUT:
<Report>
<PurchaseOrder OrderId="Order 1" Name="name1" Seller="abc">
<Reason>
<errors>
<e id="0" message="failed test ord" />
</errors>
</Reason>
<Users FirstName="abc" LastName="xyz"/>
<LineItem ProductId="Clothes1" Quantity="1.0000" SalePrice="100rs"/>
</PurchaseOrder>
</Report>
<Report>
<PurchaseOrder OrderId="Order 2" Name="name 2" Seller="abc">
<Reason>
<errors>
<e id="0" message="failed test ord" />
</errors>
</Reason>
<Users FirstName="abc" LastName="xyz"/>
<LineItem ProductId="Clothes1" Quantity="1.0000" SalePrice="100rs"/>
</PurchaseOrder>
</Report>
One way to get
<Report>
<PurchaseOrder ... >
...
</PurchaseOrder>
</Report>
<Report>
<PurchaseOrder ...>
...
</PurchaseOrder>
</Report>
structure is to wrap your select list into CROSS APLLY. Kind of
SELECT t.PurchaseOrder
FROM [Orders].dbo.OrForms f
JOIN [Orders].dbo.PurOrd p on f.GroupId = p.GroupId
CROSS APPLY (
SELECT
-- original select list copy
p.Name as "#Name",
p.Customer "#Customer",
CASE p.Name
WHEN 'Default' THEN convert(xml,p.RejectedReason)
ELSE convert(xml,f.RejectedReason)
END AS "RejectedReason",
(
SELECT u.first_name as "#FirstName",
u.last_name as "#LastName",
FROM [Users].dbo.Users u
WHERE u.user_id = u.user_id
for xml PATH('Users'), type
),
(
SELECT
li.Product as "#PId",
li.Quantity as "#Quantity",
li.SalePrice as "#Price",
FROM [Cart].dbo.LineItems li
WHERE li.OrderFormId = f.OrderFormId
ORDER BY li.ItemNumber
for xml PATH('LineItem'), type
)
-- original select list copy END
FOR XML PATH(''), TYPE
) t(PurchaseOrder)
WHERE
(p.Status = 'OrderRejected' AND p.ReportStatus IS NULL)
FOR XML PATH('Report'), TYPE;

Extracting data from XML Array using SQL

I have the following XML and would like to extract the PrimaryTeams, SecondaryTeams and OverflowTeams arrays from this and either have them comma separated or one per row.
I have the following xml:
declare #xml xml
set #xml = '<SimpleStrategy xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Synthesys.Switch.ACD">
<Id>00000000-0000-0000-0000-000000000000</Id>
<Name>Default</Name>
<AcceptedCLIs xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:string>07811353995</d2p1:string>
</AcceptedCLIs>
<ActiveHours>
<FridayEnd />
<FridayStart />
<MondayEnd />
<MondayStart />
<SaturdayEnd />
<SaturdayStart />
<SundayEnd />
<SundayStart />
<ThursdayEnd />
<ThursdayStart />
<TuesdayEnd />
<TuesdayStart />
<UseIndividualWeekDays>false</UseIndividualWeekDays>
<WednesdayEnd />
<WednesdayStart />
<WeekdayEnd />
<WeekdayStart />
</ActiveHours>
<AgentUserName />
<AllowRouteDuringFinalMessage>false</AllowRouteDuringFinalMessage>
<CRMPrefix />
<DirectDDIMessage />
<DirectDDIPassThrough>false</DirectDDIPassThrough>
<EmergencyBusyBack>false</EmergencyBusyBack>
<EmergencyDivertNumber />
<EmergencyWavFile />
<FinallyDivertNumber />
<FinallyDrop>true</FinallyDrop>
<FinallyMessageFile />
<MaximumQueueLength>0</MaximumQueueLength>
<MaximumQueueWait>0</MaximumQueueWait>
<MinimumRingTime>4000</MinimumRingTime>
<MusicOnHold />
<MusicWhileWaiting />
<NumberOfRings>2</NumberOfRings>
<OutOfHoursDivertNumber />
<OutOfHoursDrop>true</OutOfHoursDrop>
<OutOfHoursMessage />
<OverflowMessage />
<OverflowTeams xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<PrimaryTeams xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:int>3</d2p1:int>
<d2p1:int>1</d2p1:int>
</PrimaryTeams>
<Priority>1</Priority>
<RecordAgent>false</RecordAgent>
<RecordCall>true</RecordCall>
<RecordCustomer>false</RecordCustomer>
<RegulatoryMessage>Default.wav</RegulatoryMessage>
<SecondaryOverflowMessage />
<SecondaryTeams xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<SendBusyIfQueueTooLong>false</SendBusyIfQueueTooLong>
<SendBusyIfWaitTooLong>false</SendBusyIfWaitTooLong>
<TimeInOverflow>-1</TimeInOverflow>
<TimeWithDirectDDI>20000</TimeWithDirectDDI>
<TimeWithPrimaryTeams>-1</TimeWithPrimaryTeams>
<TimeWithSecondaryTeams>20000</TimeWithSecondaryTeams>
<UseDirectDDI>false</UseDirectDDI>
<UsePAM>false</UsePAM>
<UseSecondaryTeams>false</UseSecondaryTeams>
<WrapTime>40000</WrapTime>
</SimpleStrategy>'
I then created the following SQL Statement to try and extract the Teams
;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' as i, 'http://schemas.microsoft.com/2003/10/Serialization/Arrays' as d2p1,
DEFAULT 'http://schemas.datacontract.org/2004/07/Synthesys.Switch.ACD')
SELECT #xml,
#xml.value('(/SimpleStrategy/Name)[1]', 'varchar(255)'),
#xml.value('(/SimpleStrategy/PrimaryTeams)[1]', 'int') as PrimaryTeams,
#xml.value('(/SimpleStrategy/SecondaryTeams)[1]', 'int') as SecondaryTeams,
#xml.value('(/SimpleStrategy/OverflowTeams)[1]', 'int') as OverflowTeams
But all I get is the TeamID's concatenated together.
,PrimaryTeams,SecondaryTeams,OverflowTeams
Default,31,0,0
Any ideas?
Thanks
Matt
Your XML shows two team IDs in <PrimaryTeams>, while both other team nodes are empty... You did not tell us anything about the expected counts in there. However, the following approach will return a kind of entity-value-pairs with all IDs for all Teams. Hope this is what you need:
;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' as i, 'http://schemas.microsoft.com/2003/10/Serialization/Arrays' as d2p1,
DEFAULT 'http://schemas.datacontract.org/2004/07/Synthesys.Switch.ACD')
SELECT 'Name' AS Caption
,1 AS RowInx
,#xml.value('(/SimpleStrategy/Name)[1]', 'varchar(255)') AS Content
UNION ALL
SELECT 'Primary Team'
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,t.value('.','varchar(255)')
FROM #xml.nodes('/SimpleStrategy/PrimaryTeams/d2p1:int') A(t)
UNION ALL
SELECT 'Secondary Team'
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,t.value('.','varchar(255)')
FROM #xml.nodes('/SimpleStrategy/SecondaryTeams/d2p1:int') A(t)
UNION ALL
SELECT 'Overflow-Team'
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,t.value('.','varchar(255)')
FROM #xml.nodes('/SimpleStrategy/OverflowTeams/d2p1:int') A(t);

FORXML SQL Group By Element

I am trying to group some elements together under one node. This is my current SQL;
declare #xml xml
set #xml = (
select (
select
'DERIVED' '#type',
m.NuixDerivedFieldName '#name', (
SELECT
NuixFieldType as 'metadata/#type',
NuixFieldName as 'metadata/#name'
from eddsdbo.MetadataMapping m1
where m1.NuixDerivedFieldName = m.NuixDerivedFieldName
for xml path ('first-non-blank'), type
)
from (select distinct NuixDerivedFieldName from eddsdbo.MetadataMapping) m
for xml path ('metadata'))
)
;WITH XMLNAMESPACES(DEFAULT 'http://nuix.com/fbi/metadata-profile')
select #xml for XML PATH ('metadata-list'), ROOT ('metadata-profile')
Which gives me the following output;
<metadata-profile xmlns="http://nuix.com/fbi/metadata-profile">
<metadata-list>
<metadata type="DERIVED" name="Barcode" xmlns="">
<first-non-blank>
<metadata type="CUSTOM" name="Barcode" />
</first-non-blank>
<first-non-blank>
<metadata type="EVIDENCE" name="Barcode" />
</first-non-blank>
</metadata>
I want to group together elements together which have the same 'name' attribute of the metadata element under the <first-non-blank> element.
The desired output should be;
<metadata-profile xmlns="http://nuix.com/fbi/metadata-profile">
<metadata-list>
<metadata type="DERIVED" name="Barcode" xmlns="">
<first-non-blank>
<metadata type="CUSTOM" name="Barcode" />
<metadata type="EVIDENCE" name="Barcode" />
</first-non-blank>
</metadata>
...
My database looks something like this;
NuixFieldName NuixFieldType NuixDerivedFieldName
------------------------------ ------------------------------ ------------------------------
_EmailEntryID PROPERTY EmailEntryID
Audited Audited Audited
Author PROPERTY Author
Barcode CUSTOM Barcode
Barcode EVIDENCE Barcode
I would also like to remove the xlmns namespace identifier from the metadata elements.
Thanks in advance!
You could try this
DECLARE #SampleData AS TABLE
(
NuixFieldName varchar(20),
NuixFieldType varchar(20),
NuixDerivedFieldName varchar(20)
)
INSERT INTO #SampleData
VALUES
('_EmailEntryID','PROPERTY','EmailEntryID'),
('Audited', 'Audited ','Audited'),
('Author ', 'PROPERTY','Author '),
('Barcode', 'CUSTOM ','Barcode'),
('Barcode', 'EVIDENCE','Barcode')
DECLARE #xml XML
SET #xml = (
SELECT
-- sd.NuixDerivedFieldName AS [#name],
'DERIVED' AS [#type],
sd.NuixDerivedFieldName AS [#name],
(
SELECT
sd2.NuixFieldType as '#type',
sd2.NuixFieldName as '#name'
FROM #SampleData sd2 WHERE sd2.NuixDerivedFieldName = sd.NuixDerivedFieldName
FOR XML PATH ('metadata'),ROOT('first-non-blank'), TYPE
)
FROM (select DISTINCT sd.NuixDerivedFieldName from #SampleData sd ) sd
FOR XML PATH('metadata'), ROOT('metadata-list'),TYPE
)
;WITH XMLNAMESPACES(DEFAULT 'http://nuix.com/fbi/metadata-profile')
SELECT #xml FOR XML PATH (''),ROOT('metadata-profile')
return:
<metadata-profile xmlns="http://nuix.com/fbi/metadata-profile">
<metadata-list>
<metadata type="DERIVED" name="Audited">
<first-non-blank>
<metadata type="Audited " name="Audited" />
</first-non-blank>
</metadata>
<metadata type="DERIVED" name="Author ">
<first-non-blank>
<metadata type="PROPERTY" name="Author " />
</first-non-blank>
</metadata>
<metadata type="DERIVED" name="Barcode">
<first-non-blank>
<metadata type="CUSTOM " name="Barcode" />
<metadata type="EVIDENCE" name="Barcode" />
</first-non-blank>
</metadata>
<metadata type="DERIVED" name="EmailEntryID">
<first-non-blank>
<metadata type="PROPERTY" name="_EmailEntryID" />
</first-non-blank>
</metadata>
</metadata-list>
</metadata-profile>

Searching for "|" pipe symbol in XML column in SQL server

I am using a script like this to look for a "|" symbol in a XML column no matter where it appears. I know there are pipes in there but the below query gives me empty results
SELECT TOP 100 *
FROM
[DB].[dbo].[InputData]
WHERE
Content.exist('//.[text() = "|"]') = 1
AND DataFileId = '75d48aed6327'
What am I doing wrong? This is the xml content of the column Content:
<CLAIM version="native">
<INPUT>
<HEADER BIRTH_DT="1/1/1941">
<DIAG_CODES>
<DX CODE="7234" />
</DIAG_CODES>
<CON_CODES>
<CON_CODE VALUE="M0" />
</CON_CODES>
<VAL_CODES>
<VAL_CODE CODE="A2" AMT="604.03" />
</VAL_CODES>
</HEADER>
</CLAIM>
Hi was looking the answer and found it here https://stackoverflow.com/a/11738172/1692632
DECLARE #xmlTable TABLE (xmlData XML)
INSERT INTO #xmlTable SELECT N'
<CLAIM version="native">
<HEADER BIRTH_DT="1/1/1941">
<DIAG_CODES>
<DX CODE="7234" />
</DIAG_CODES>
<CON_CODES>
<CON_CODE VALUE="M0" />
</CON_CODES>
<VAL_CODES>
<VAL_CODE CODE="A2" AMT="604.03" />
</VAL_CODES>
</HEADER>
</CLAIM>
'
INSERT INTO #xmlTable SELECT N'
<CLAIM version="native">
<HEADER BIRTH_DT="1/1/1941">
<DIAG_CODES>
<DX CODE="72|34" />
</DIAG_CODES>
<CON_CODES>
<CON_CODE VALUE="M0" />
</CON_CODES>
<VAL_CODES>
<VAL_CODE CODE="A2" AMT="604.03" />
</VAL_CODES>
</HEADER>
</CLAIM>
'
SELECT T.*
FROM #xmlTable AS T
CROSS APPLY T.xmlData.nodes('//#*') as tx(r)
WHERE tx.r.value('contains((.),"|")','bit')=1
Also you can try this one as you tried (this gives without duplicates):
SELECT TOP 100 *
FROM
#xmlTable
WHERE
xmlData.exist('//#*[contains(., "|")]') = 1
SELECT TOP 100 *
FROM [DB].[dbo].[InputData]
WHERE DataFileId = '75d48aed6327'
and charindex('|',Content) > 1

SQL xml query returns null

I have the following XML:
<Matter>
<CriticalDates>
<CriticalDate>
<CriticalDateId>2</CriticalDateId>
<Name>Instruction Date</Name>
<Value_FieldId>9F21</Value_FieldId>
<Confirmed_FieldId />
<Status>In Progress</Status>
<ConfirmStatus />
<CompPercent>0</CompPercent>
<Order>0</Order>
<Value>2014-03-28T06:00:00+11:00</Value>
<Confirmed>false</Confirmed>
</CriticalDate>
<CriticalDate>
<CriticalDateId>-2</CriticalDateId>
<Name>Completion Date</Name>
<Value_FieldId>9F22</Value_FieldId>
<Confirmed_FieldId>9F27</Confirmed_FieldId>
<Status>Complete</Status>
<ConfirmStatus />
<CompPercent>0</CompPercent>
<Order>1</Order>
<Value />
<Confirmed>false</Confirmed>
</CriticalDate>
<CriticalDate>
<CriticalDateId>-3</CriticalDateId>
<Name>Not Proceeding Date</Name>
<Value_FieldId>9F23</Value_FieldId>
<Confirmed_FieldId />
<Status>Not Proceeding</Status>
<ConfirmStatus />
<CompPercent>0</CompPercent>
<Order>2</Order>
<Value />
<Confirmed>false</Confirmed>
</CriticalDate>
</CriticalDates>
</Matter>
To select all the nodes as rows i'm using:
SELECT
MatterId,
MatterXml,
MD.CD.value('(Name)[1]', 'VARCHAR(50)') AS 'Name',
MD.CD.value('(Status)[1]', 'VARCHAR(50)') AS 'Status',
MD.CD.value('(value)[1]', 'DATE') AS 'CriticalDate',
MD.CD.value('(Confirmed)[1]', 'VARCHAR(50)') AS 'Confirmed'
FROM
dbo.Matter m
CROSS APPLY m.MatterXml.nodes('/Matter/CriticalDates/CriticalDate') AS MD(CD)
When i run this i get 3 rows back but all CriticalDates return as NULL even the first one when there is a date in the XML. Please help!
XML is case sensitive. Try is with:
MD.CD.value('(Value)[1]', 'DATE') AS 'CriticalDate',