DB2 build xml document from query - stored-procedures

In my stored procedure, I want to build a single xml document. The query below creates 100 documents. Is it possible to declare one document which has 100 elements of type elem?
SELECT XMLELEMENT (NAME "elem",
XMLAttributes(Identifier as "ID", Name AS "name"))
FROM
(
SELECT a.*
FROM OBJECTS a
FETCH FIRST 100 ROWS ONLY
)

You are probably looking for the XMLAGG function. It aggregates XML snippets. One use case is to wrapt multiple XMLELEMENTs into a single document. This example is taken from the linked documentation:
SELECT XMLSERIALIZE(
CONTENT XMLELEMENT(
NAME "Department", XMLATTRIBUTES(
E.WORKDEPT AS "name"
),
XMLAGG(
XMLELEMENT(
NAME "emp", E.LASTNAME
)
ORDER BY E.LASTNAME
)
)
AS CLOB(110)
)
AS "dept_list"
FROM EMPLOYEE E
WHERE E.WORKDEPT IN ('C01','E21')
GROUP BY WORKDEPT
In your case, EMPLOYEE would be OBJECTS. The query structure is very similar.

Related

Populating Fact Tables(Data Warehouse) and Querying

I am not sure how to query my fact tables(covid and vaccinations), I populated the dimensions with dummy data, I am supposed to leave the fact tables empty? As far as I know, they would get populated when I write the queries.
I am not sure how to query the tables I have tried different things, but I get an empty result.
Below is a link to the schema.
I want to find out the "TotalDeathsUK"(fact table COVID) for the last year caused by each "Strain"(my strain table has 3 strain in total.
You can use MERGE to poulate your fact table COVIDFact :
MERGE
INTO factcovid
using (
SELECT centerid,
dateid,
patientid,
strainid
FROM yourstagingfacttable ) AS f
ON factcovid.centerid = f.centerid AND factcovid.dateid=f.dateid... //the join columns
WHEN matched THEN
do nothing WHEN NOT matched THEN
INSERT VALUES
(
f.centerid,
f.dateid,
f.patientid,
f.strainid
)
And for VaccinationsFact :
MERGE
INTO vaccinations
using (
SELECT centerid,
dateid,
patientid,
vaccineid
FROM yourstagingfacttable ) AS f
ON factcovid.centerid = f.centerid //join condition(s)
WHEN matched THEN
do nothing WHEN NOT matched THEN
INSERT VALUES
(
f.centerid,
f.dateid,
f.patientid,
f.vaccineid
)
For the TotalDeathUK measure :
SELECT S.[Name] AS Strain, COUNT(CF.PatientID) AS [Count of Deaths] FROM CovidFact AS CF
LEFT JOIN Strain AS S ON S.StrainID=CF.StrainID
LEFT JOIN Time AS T ON CF.DateID=T.DateID
LEFT JOIN TreatmentCenter AS TR ON TR.CenterID=CF.CenterID
LEFT JOIN City AS C ON C.CityID = TR.CityID
WHERE C.Country LIKE 'UK' AND T.Year=2020
AND Result LIKE 'Death' // you should add a Result column to check if the Patient survived or died
GROUP BY S.[Name]

MDX join with the same dimension

I'm writing some MDX to join a dimension to itself based on two different periods to get a common list, then do a count against this list for both.
In short, I need to
get a list of Student.UniqueId's for Period1 which has a flag (IsValid) that is set that isn't set within the Period2 data
get a full list of Students for Period2
join the two lists and produce two records (one for each period) with the same count (these counts will be used for calculated member calculations within each period)
I have tried doing it via subselect and exists clause with filter
SELECT
{
[Measures].[FactStudentCount]
} on COLUMNS,
{ NONEMPTY
(
[TestEvent].[TestEvents].[Name].ALLMEMBERS
* [TestEvent].[PeriodName].[PeriodName].ALLMEMBERS
)
} ON ROWS
FROM ( SELECT ( {
exists
(
filter([Student].[UniqueId].[UniqueId].MEMBERS
,([TestEvent].[Key].&[Period1], [IsValid].[Code].&[Yes]))
,
filter([Student].[UniqueId].[UniqueId].MEMBERS
,[TestEvent].[Key].&[Period2])
)
}) ON COLUMNS
FROM [MyCube])
...however this doesn't give the correct result
(To obtain context) I have also tried similar exists/filter within a where clause
SELECT
{
[Measures].[FactStudentCount]
} on COLUMNS,
{ NONEMPTY
(
[TestEvent].[TestEvents].[Name].ALLMEMBERS
* [TestEvent].[PeriodName].[PeriodName].ALLMEMBERS
)
} ON ROWS
FROM [MyCube]
where (
exists
(
filter([Student].[UniqueId].[UniqueId].MEMBERS
,([TestEvent].[Key].&[Period1], [IsValid].[Code].&[Yes]))
,
filter([Student].[UniqueId].[UniqueId].MEMBERS
,[TestEvent].[Key].&[Period2])
)
)
...however again this doesn't produce the correct result
I have tried tweaking the filter statements (within the exists) to something like
(filter(existing([Student].[UniqueId].[UniqueId].allmembers),[TestEvent].[Key].CurrentMember.MemberValue = 'Period1'), [IsValid].[Code].&[Yes])
,
(filter(existing([Student].[UniqueId].[UniqueId].allmembers),[TestEvent].[Key].CurrentMember.MemberValue = 'Period2'))
...however this only returns one row (for Period1) - that said it is the correct total
I have also tried via a CrossJoin with NonEmpty however it fails because the fields come from the same hierarchy - the message "The Key hierarchy is used more than once in the Crossjoin function"
Does any one have any insight into how to resolve the above scenario ?
This is what I did
NonEmpty(
NonEmpty(
{([Student].[UniqueId].[UniqueId].members)},{([TestEvent].[Key].&[Period1], [IsValid].[Code].&[Yes])}
)
,
{([Student].[UniqueId].[UniqueId].members,[TestEvent].[Key].&[Period2])}
)
This gets all Period1 elements, with IsValid='Yes' then 'left joins' this with records in Period2

Use computed columns in related table in power query on SQLITE via odbc

In power query ( version included with exel 2016, PC ), is it possible to refer to a computed column of a related table?
Say I have an sqlite database as follow
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE products (
iddb INTEGER NOT NULL,
px FLOAT,
PRIMARY KEY (iddb)
);
INSERT INTO "products" VALUES(0,0.0);
INSERT INTO "products" VALUES(1,1.1);
INSERT INTO "products" VALUES(2,2.2);
INSERT INTO "products" VALUES(3,3.3);
INSERT INTO "products" VALUES(4,4.4);
CREATE TABLE sales (
iddb INTEGER NOT NULL,
quantity INTEGER,
product_iddb INTEGER,
PRIMARY KEY (iddb),
FOREIGN KEY(product_iddb) REFERENCES products (iddb)
);
INSERT INTO "sales" VALUES(0,0,0);
INSERT INTO "sales" VALUES(1,1,1);
INSERT INTO "sales" VALUES(2,2,2);
INSERT INTO "sales" VALUES(3,3,3);
INSERT INTO "sales" VALUES(4,4,4);
INSERT INTO "sales" VALUES(5,5,0);
INSERT INTO "sales" VALUES(6,6,1);
INSERT INTO "sales" VALUES(7,7,2);
INSERT INTO "sales" VALUES(8,8,3);
INSERT INTO "sales" VALUES(9,9,4);
COMMIT;
basically we have products ( iddb, px ) and sales of those products ( iddb, quantity, product_iddb )
I load this data in power query by:
A. creating an ODBC data source using SQLITE3 driver : testDSN
B. in excel : data / new query , feeding this connection string Provider=MSDASQL.1;Persist Security Info=False;DSN=testDSN;
Now in power query I add a computed column, say px10 = px * 10 to the product table.
In the sales table, I can expand the product table into product.px, but not product.px10 . Shouldn't it be doable? ( in this simplified example I could expand first product.px and then create the px10 column in the sales table, but then any new table needinng px10 from product would require me to repeat the work... )
Any inputs appreciated.
I would add a Merge step from the sales query to connect it to the product query (which will include your calculated column). Then I would expand the Table returned to get your px10 column.
This is instead of expanding the Value column representing the product SQL table, which gets generated using the SQL foreign key.
You will have to come back and add any further columns added to the product query to the expansion list, but at least the column definition is only in one place.
In functional programming you don't modify existing values, only create new values. When you add the new column to product it creates a new table, and doesn't modify the product table that shows up in related tables. Adding a new column over product can't show up in Odbc's tables unless you apply that transformation to all related tables.
What you could do is generalize the "add a computed column" into a function that takes a table or record and adds the extra field. Then just apply that over each table in your database.
Here's an example against Northwind in SQL Server
let
Source = Sql.Database(".", "Northwind_Copy"),
AddAColumn = (data) => if data is table then Table.AddColumn(data, "UnitPrice10x", each [UnitPrice] * 10)
else if data is record then Record.AddField(data, "UnitPrice10x", data[UnitPrice] * 10)
else data,
TransformedSource = Table.TransformColumns(Source, {"Data", (data) => if data is table then Table.TransformColumns(data, {"Products", AddAColumn}, null, MissingField.Ignore) else data}),
OrderDetails = TransformedSource{[Schema="dbo",Item="Order Details"]}[Data],
#"Expanded Products" = Table.ExpandRecordColumn(OrderDetails, "Products", {"UnitPrice", "UnitPrice10x"}, {"Products.UnitPrice", "Products.UnitPrice10x"})
in
#"Expanded Products"

How to include two SSAS cube fields of same name in SSRS report

I am using the query designer tool within SSRS to build an MDX query against the TFS analysis cube. I am trying to query some data within TFS 2013. My query returns what I need, however, when I attempt to close the query designer to return to my report I get the following error message:
The query contains more than one unnamed or duplicate field name.
Please specify unique column aliases
The problem is that I want to include the System_Title field from the Work Item tree and the System_Title field from the Work Item Linked tree.
How can I include both of these System_Title fields in my SSRS report?
Here is my MDX for reference
SELECT NON EMPTY { [Measures].[Microsoft_VSTS_Scheduling_RemainingWork]
, [Measures].[Microsoft_VSTS_Scheduling_OriginalEstimate]
, [Measures].[Microsoft_VSTS_Scheduling_CompletedWork] }
ON COLUMNS
, NON EMPTY { ([Work Item].[System_AssignedTo].[System_AssignedTo].ALLMEMBERS
* [Work Item].[System_WorkItemType].[System_WorkItemType].ALLMEMBERS
* [Work Item].[Iteration Path].[Iteration Path].ALLMEMBERS
* [Work Item].[Area Path].[Area Path].ALLMEMBERS
* [Work Item Linked].[System_Title].[System_Title].ALLMEMBERS
* [Work Item].[System_Title].[System_Title].ALLMEMBERS
* [Work Item].[System_State].[System_State].ALLMEMBERS ) }
DIMENSION PROPERTIES MEMBER_CAPTION
, MEMBER_UNIQUE_NAME ON ROWS FROM (
SELECT ( STRTOSET(#WorkItemIterationPath, CONSTRAINED) )
ON COLUMNS
FROM (
SELECT ( STRTOSET(#WorkItemAreaPath, CONSTRAINED) )
ON COLUMNS FROM [Team System]))
CELL PROPERTIES VALUE
, BACK_COLOR, FORE_COLOR
, FORMATTED_VALUE
, FORMAT_STRING
, FONT_NAME
, FONT_SIZE
, FONT_FLAGS
By default your field names are set to the attribute name. You can change this by going to the field names and putting in a different field name for one of the two System_Title fields.
Right click on the data set in the Datasets folder.
Choose Dataset Properties.
Go to the Fields page.
Change the Field Name.
Click OK.

Query for Work Items that contain attachments of a specific type

In our releases sometimes it is necessary to run scripts on our production db. The standard has been to attach a .sql file to the work item if a script needs to be run on the db.
Is there any way I can query for Work Items which contain an attachment that is a .sql file? I'd prefer not to have to open each Work Item to check for these attachments each time I need to push a release.
This how I did it by querying the TfsWorkItemTracking database directly. I would assume that Fld10005 may or may not be the same on other instances of TFS. The fields can be found in the dbo.Fields table.
with [project-nodes] (
ID,
[Name],
[Path],
[Depth])
as (
select
t.ID,
t.Name,
cast(t.Name as varchar(max)) as [Path],
0 as [Depth]
from dbo.TreeNodes t
where t.ID = 220
union all
select
c.ID,
c.Name,
cast(p.[Path] + '/' + c.Name as varchar(max)),
[Depth] + 1
from dbo.TreeNodes c
inner join [project-nodes] p
on c.ParentID = p.ID)
select
t.[Path] as [Area Path],
l.Title,
l.Fld10005 as [Resolved Date],
f.OriginalName
from dbo.WorkItemsLatest l
inner join [project-nodes] t
on l.AreaID = t.ID
inner join dbo.WorkItemFiles f
on l.ID = f.ID
where f.OriginalName like '%.sql'
and l.Fld10005 > '2010-05-21' -- ResolvedDate
order by
t.Name
Another way you could do this through WIQL, but still maintain some level of performance is to create a custom work item type that you would use for these tasks. Then, if you set up your WIQL query to be
(semi-pseudocode)
SELECT Id from WorkItems where
WorkItemType = 'MySpecialWorkItem' AND
Status = 'Active';
You could then iterate through and check for the attached SQL files. Using the WorkItemType and Status criteria should limit the number of returned records significantly, then iterating through attachments won't affect performance of the process all that much.

Resources