Split XML report in SQL - 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;

Related

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>

Using SQL to Generate XML

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>

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',

FOR XML multiple control by attribute in tree concept

I want to figure out one issue.
I already had question about simple ordering issue but I want to order more detail.
check below this link :
SQL Server : FOR XML sorting control by attribute
I made a example case.
SQL Query.
select (
select '123' AS '#id', (
select
(
select 'test' AS '#testid' , '20' AS '#order'
FOR XML path ('tree') , TYPE
),
(
select 'test2' AS '#testid' , '30' AS '#order'
FOR XML path ('tree-order') , TYPE
),
(
select 'test' AS '#testid' , '10' AS '#order'
FOR XML path ('tree') , TYPE
)
FOR XML path ('Node') , TYPE
)
FOR XML path ('Sample') , TYPE
),
(select '456' AS '#id', (
select
(
select 'test' AS '#testid' , '20' AS '#order'
FOR XML path ('tree') , TYPE
),
(
select 'test2' AS '#testid' , '30' AS '#order'
FOR XML path ('tree-order') , TYPE
),
(
select 'test' AS '#testid' , '10' AS '#order'
FOR XML path ('tree') , TYPE
)
FOR XML path ('Node') , TYPE
)
FOR XML path ('Sample') , TYPE)
FOR XML path ('Main') , TYPE
Result :
<Main>
<Sample id="123">
<Node>
<tree testid="test" order="20" />
<tree-order testid="test2" order="30" />
<tree testid="test" order="10" />
</Node>
</Sample>
<Sample id="456">
<Node>
<tree testid="test" order="20" />
<tree-order testid="test2" order="30" />
<tree testid="test" order="10" />
</Node>
</Sample>
</Main>
Expected result :
<Main>
<Sample id="123">
<Node>
<tree testid="test" order="10" />
<tree testid="test" order="20" />
<tree-order testid="test2" order="30" />
</Node>
</Sample>
<Sample id="456">
<Node>
<tree testid="test" order="10" />
<tree testid="test" order="20" />
<tree-order testid="test2" order="30" />
</Node>
</Sample>
</Main>
final result :
<Main>
<Sample id="123">
<Node>
<tree testid="test" />
<tree testid="test" />
<tree-order testid="test2" />
</Node>
</Sample>
<Sample id="456">
<Node>
<tree testid="test" />
<tree testid="test" />
<tree-order testid="test2" />
</Node>
</Sample>
</Main>
That's order by tree-order.
finally I don't want to show order information in attribute
Any one has great Idea?
Thank you for everybody who interesting to this.
Updated ----------------------------------------
Thank you every body finally I solved problem as below about order by and remove attribute issue :
declare #resultData xml = (select #data.query('
element Main {
for $s in Main/Sample
return element Sample {
$s/#*,
for $n in $s/Node
return element Node {
for $i in $n/*
order by $i/#order
return $i
}
}
}'));
SET #resultData.modify('delete (Main/Sample/Node/tree/#order)');
SET #resultData.modify('delete (Main/Sample/Node/tree-order/#order)');
select #resultData
select #data.query('
element Main {
for $s in Main/Sample
return element Sample {
$s/#*,
for $n in $s/Node
return element Node {
for $i in Node/*
order by $i/#order
return
if ($i/self::tree)
then element tree { $i/#testid }
else element tree-order { $i/#testid }
}
}
}
}')
What's interesting to me is that in your original post, you're stating that you're generating the XML as the result of a SQL query. If it were me, I'd control the ordering at that level.

tsql code to sync two xml documents

xml 1
<Team>
<Players>
<Player id="1" timestamp="11/03/2012 08:10:12">30</Player>
<Player id="2" timestamp="11/02/2012 09:11:12">40</Player>
<Players>
</Team>
xml 2
<Team>
<Players>
<Player id="1" timestamp="11/02/2012 09:10:12">10</Player>
<Player id="2" timestamp="11/03/2012 10:11:12">20</Player>
<Player id="3" timestamp="11/03/2012 13:00:00">50</Player>
<Players>
</Team>
OUTPUT when we merge the above two XMLs based on timestamp element:
<Team>
<Players>
<Player id="1" timestamp="11/02/2012 09:10:12">30</Player>
<Player id="2" timestamp="11/03/2012 10:11:12">20</Player>
<Player id="3" timestamp="11/03/2012 13:00:00">50</Player>
<Players>
</Team>
Could anyone please help me with T-SQL code to do this in SQL Server 2005/2008
Using this approach with two nested CTE's, you can get close - but not all the way:
DECLARE #XML1 XML = '<Team>
<Players>
<Player id="1" timestamp="11/03/2012 08:10:12">30</Player>
<Player id="2" timestamp="11/02/2012 09:11:12">40</Player>
</Players>
</Team>'
DECLARE #XML2 XML = '<Team>
<Players>
<Player id="1" timestamp="11/02/2012 09:10:12">10</Player>
<Player id="2" timestamp="11/03/2012 10:11:12">20</Player>
<Player id="3" timestamp="11/03/2012 13:00:00">50</Player>
</Players>
</Team>'
-- extract the ID, Timestamp and node values from both XML variables
;WITH CTE AS
(
SELECT
ID = P1.value('#id', 'int'),
TS = P1.value('#timestamp', 'datetime2'),
NodeValue = P1.value('(.)[1]', 'int')
FROM #XML1.nodes('/Team/Players/Player') AS XTbl1(P1)
UNION
SELECT
ID = P2.value('#id', 'int'),
TS = P2.value('#timestamp', 'datetime2'),
NodeValue = P2.value('(.)[1]', 'int')
FROM #XML2.nodes('/Team/Players/Player') AS XTbl2(P2)
),
-- partition and sequentially number the result, so that the newest
-- (most recent) item can be extracted
CTE2 AS
(
SELECT ID, TS, NodeValue,
RowNum = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY TS DESC)
FROM CTE
)
SELECT
ID AS '#id',
TS AS '#timestamp',
NodeValue AS 'text()'
FROM CTE2
WHERE RowNum = 1
FOR XML PATH('Player'), ROOT('Players')
Results in output like this:
<Players>
<Player id="1" timestamp="2012-11-03T08:10:12">30</Player>
<Player id="2" timestamp="2012-11-03T10:11:12">20</Player>
<Player id="3" timestamp="2012-11-03T13:00:00">50</Player>
</Players>