How to sort specific Hierarchy Level in OLAP (SSAS) via MDX - sorting

I'm using SSAS OLAP and I want to apply sorting of the levels of a hierarchy.
I know that I can sort the whole hierarchy via ORDER function (I have some issues when I'm trying to apply DESC sorting on the whole hierarchy), but what I really want to achieve is sorting of a specific level. For example in the [Date].[Calendar] hierarchy (Adventure Works Cube), I want to have ASC sorting of years, DESC sorting of Quarter, ASC sorting of Months, etc.
I do not want to break the hierarchy (using BASC or BDESC), I just need them sorted on the same level. Do you have an idea if this is possible at all, as I was unable to find anything simillar?

What you can do is crossjoining the ordered correspoding attribute hierarchies. Could you verify the results of the following:
select
[Measures].[Internet Sales Amount] on 0,
Order(
[Date].[Calendar Year].[Calendar Year].MEMBERS,
[Measures].[Internet Sales Amount]
,ASC)
*
Order(
[Date].[Calendar Quarter of Year].[Calendar Quarter of Year].MEMBERS,
[Measures].[Internet Sales Amount]
,DESC)
*
Order(
[Date].[Calendar].[Month].MEMBERS,
[Measures].[Internet Sales Amount]
,ASC)
ON 1
from [Adventure Works]
Including the all members could help ([Date].[Calendar Year] instead of [Date].[Calendar Year] in case of the first crossjoin set)
Philip,

The following is a working solution, if not a really nice one:
WITH MEMBER Measures.[year] as
[Date].[Calendar].CurrentMember.Properties('Key0', typed)
MEMBER Measures.[qtr or mth] as
[Date].[Calendar].CurrentMember.Properties('Key1', typed)
MEMBER Measures.[sortKey] as
CASE
WHEN [Date].[Calendar].CurrentMember.Level IS [Date].[Calendar].[Calendar Year] THEN
Measures.[year] * 1000
WHEN [Date].[Calendar].CurrentMember.Level IS [Date].[Calendar].[Calendar Quarter] THEN
Measures.[year] * 1000 + (5 - Measures.[qtr or mth]) * 100
ELSE // month
Measures.[year] * 1000 + (4 - Int((Measures.[qtr or mth] - 1) / 3)) * 100 + Measures.[qtr or mth]
END
SELECT {Measures.[year], Measures.[qtr or mth], Measures.[sortKey]}
ON COLUMNS,
Order(
[Date].[Calendar].[Calendar Year]
+
[Date].[Calendar].[Calendar Quarter]
+
[Date].[Calendar].[Month]
,
Measures.[sortKey]
,BASC
)
ON ROWS
FROM [Adventure Works]
The measures on the columns are just for clarification how it works. And the main logic is in the generation of the sortKey measure which generates an integer key as follows: four digits for the year, one digit for the backwards quarter (Q4 -> 1, ... Q1 -> 4), two digits for the month. Then, using that, it is straightforward to call Order to return the sorted set.

It turned out that it is impossible to achieve the desired effect - there's no such MDX query that can sort different levels with different sorting type. More information is available in this thread.

Related

TOPN DAX function not working. The formula is returning all the rows and not TOP 3

My goal is to create measure to get top 3 customer Names and there respective sales.
I am using the below measure to bring top 3 names along with there sales. The below measure is returning all the rows. I fail to understand why this is happening and why filtering is not happening for top 3 customers
topN = calculate(sum(Sale[Total Excluding Tax]),
TOPN(3,
values(Sale[Employee Name]),
calculate(sum(Sale[Total Excluding Tax]))
)
)
Sale[Employee Name] is calculated column and is coming from another table Employee by using Employee Name = RELATED(Employee[Employee])
The DAX is working properly and grabbing top 3 records. Order/sorting is important. You need to order your results.
Create a calculate column [Total Excluding Tax] to sum up the Total excluding tax. Then use that column in a measure; try something like:
Top Sales = TOPN ( 3, ALLSELECTED( 'Sale' ), [Total Excluding Tax]), desc)

Max value from Member Property

I need to get the max value of a member property to use in another MDX expression.
As an example from Adventure Works I'm using the following
WITH
MEMBER DoFP AS
[Customer].[Customer].Properties("Date of First Purchase")
MEMBER MaxDoFP AS
Tail
(
NonEmpty
(
[Date].[Date].[Date]
,[Measures].[DoFP]
)
,1
).Item(0).MemberValue
SELECT
[Date].[Calendar Year].[Calendar Year] ON 0
,{
[Customer].[Customer].&[20075]
,[Customer].[Customer].&[15568]
,[Customer].[Customer].&[20285]
} ON 1
FROM [Adventure works]
WHERE
[Measures].[DoFP];
I'd like it to return June 15, 2008 for all rows/cols which is the date of first purchase for Aaron Alexander (who has the max DoFP of the customers selected) so that I can do some more calcs. Instead its giving me 31st Dec 2010 because (I assume) that's the last date in my [Date].[Date].[Date].
Not pretty:
WITH
SET X AS
{
[Customer].[Customer].&[20075]
,[Customer].[Customer].&[15568]
,[Customer].[Customer].&[20285]
}
MEMBER DoFP AS
[Customer].[Customer].Properties("Date of First Purchase")
MEMBER MaxDoFP1 AS
Max(DoFP)
MEMBER MaxDoFP2 AS
Max
(
X
,Cdate([Customer].[Customer].Properties("Date of First Purchase"))
)
SELECT
[Date].[Calendar Year].[Calendar Year] ON 0
,
X * {[Measures].[MaxDoFP1],[Measures].[MaxDoFP2]} ON 1
FROM [Adventure works];
Another option (assuming you have the ability to extend the cube structure) would be to create a measure group based on the customer dimension using a MAX aggregation function. I imagine performance would be a bit better for larger sets.
WITH
SET X AS
{
[Customer].[Customer].&[20075]
,[Customer].[Customer].&[15568]
,[Customer].[Customer].&[20285]
}
MEMBER MaxDoFP AS
Max (X,[Measures].[Customer DoFP MAX])
SELECT
[Date].[Calendar Year].[Calendar Year] ON 0
,X * {[Measures].[MaxDoFP]} ON 1
FROM [Adventure works];

Trying to figure out top 5 land areas of the 50 states in the U.S

I have a table created. With one column named states and another column called land area. I am using oracle 11g. I have looked at various questions on here and cannot find a solution. Here is what I have tried so far:
SELECT LandAreas, State
FROM ( SELECT LandAreas, State, DENSE_RANK() OVER (ORDER BY State DESC) sal_dense_rank
FROM Map )
WHERE sal_dense_rank >= 5;
This does not provide the top 5 land areas as far as number wise.
I have also tried this one but no go either:
SELECT * FROM Map order by State desc)
where rownum < 5;
Anyone have any suggestions to get me on the right track??
Here is a samle of the table
states land areas
michagan 15000
florida 25000
tennessee 10000
alabama 80000
new york 150000
california 20000
oregon 5000
texas 6000
utah 3000
nebraska 1000
Desired output from query:
States land area
new york 150000
alabama 80000
florida 25000
california 20000
Try:
Select * from
(SELECT State, LandAreas FROM Map ORDER BY LandAreas DESC)
where rownum < 6
Link to Fiddle
Use a HAVING clause and count the number state states larger:
SELECT m.state, m.landArea
FROM Map m
LEFT JOIN Map m2 on m2.landArea > m.landArea
GROUP BY m.state, m.landArea
HAVING count(*) < 5
ORDER BY m.landArea DESC
See SQLFiddle
This joins each state to every state whose area is greater, then uses a HAVING clause to return only those states where the number of larger states was less than 5.
Ties are all returned, leading to more than 5 rows in the case of a tie for 5th.
The left join is needed for the case of the largest state, which has no other larger state to join to.
The ORDER BY is optional.
Try something like this
select m.states,m.landarea
from map m
where (select count(‘x’) from map m2 where m2.landarea > m.landarea)<=5
order by m.landarea
There are two bloomers in your posted code.
You need to use landarea in the DENSE_RANK() call. At the moment you're ordering the states in reverse alphabetical order.
Your filter in the outer query is the wrong way around: you're excluding the top four results.
Here is what you need ...
SELECT LandArea, State
FROM ( SELECT LandArea
, State
, DENSE_RANK() OVER (ORDER BY landarea DESC) as area_dr
FROM Maps )
WHERE area_dr <= 5
order by area_dr;
... and here is the SQL Fiddle to prove it. (I'm going with the statement in the question that you want the top 5 biggest states and ignoring the fact that your desired result set has only four rows. But adjust the outer filter as you will).
There are three different functions for deriving top-N result sets: DENSE_RANK, RANK and ROW_NUMBER.
Using ROW_NUMBER will always guarantee you 5 rows in the result set, but you may get the wrong result if there are several states with the same land area (unlikely in this case, but other data sets will produce such clashes). So: 1,2,3,4,5
The difference between RANK and DENSE_RANK is how they handle ties. DENSE_RANK always produces a series of consecutive numbers, regardless of how many rows there are in each rank. So: 1,2,2,3,3,3,4,5
RANK on the other hand will produce a sparse series if a given rank has more than one hit. So: 1,2,2,4,4,4.
Note that each of the example result sets has a different number of rows. Which one is correct? It depends on the precise question you want to ask.
Using a sorted sub-query with the ROWNUM pseudo-column will work like the ROW_NUMBER function, but I prefer using ROW_NUMBER because it is more powerful and more error-proof.

Show report horizontally with SQL or PL/SQL

I am developing a report which should display the data horizontally.
What must be shown is the following:
email#1.com 12/09/2013 11/09/2013 10/09/2013 09/09/2013...
email#2.com 22/03/2013 21/03/2013 12/02/2013 02/01/2013...
Well, I have these data organized in two tables:
Member and Report.
The Member table has the email address and the Report table has dates, and each email can have many different dates.
I can easily retrieve that information vertically:
SELECT M.EMAIL, R.LAST_OPEN_DATE
FROM MEMBER M, REPORT R
WHERE M.MEMBER_ID = R.MEMBER_ID
AND R.STATUS = 1
AND TRUNC(R.LAST_OPEN_DATE) >= TRUNC(SYSDATE) - 120;
However to show the results horizontally is complicated, anyone have a tip or know how I can do this?
I'm using Oracle 11g.
You can get the dates into columns with pivot:
SELECT *
FROM (
SELECT M.EMAIL, R.LAST_OPEN_DATE,
ROW_NUMBER() OVER (PARTITION BY M.MEMBER_ID
ORDER BY R.LAST_OPEN_DATE DESC) AS RN
FROM MEMBER M, REPORT R
WHERE M.MEMBER_ID = R.MEMBER_ID
AND R.STATUS = 1
AND TRUNC(R.LAST_OPEN_DATE) >= TRUNC(SYSDATE) - 120
)
PIVOT (MIN(LAST_OPEN_DATE) FOR (RN) IN (1, 2, 3, 4, 5, 6, 7, 8));
SQL Fiddle.
Essentially this is assigning a number to each report date for each member, and then the pivot is based on that ranking number.
But you'd need to have each of the possible number of days listed; if you can have up to 240 report dates, the PIVOT IN clause would need to be every number up to 240, i.e. IN (1, 2, 3, ..., 239, 240), not just up to eight as in that Fiddle.
If you ever had a member with more than 240 dates you wouldn't see some of them, so whatever high number you pick would have to be high enough to cover every possibility, now and in the foreseeable future. As your query is limited to 120 days, even 240 seems quite high, but perhaps you have more than one per day - in which case there is no real upper limit.
You could potentially have to format each date column individually, but hopefully your reporting layer is taking care of that.
If you just wanted to perform string aggregation using the multiple dates for each email, in you could do this in 11g:
SELECT M.EMAIL,
LISTAGG(TO_CHAR(R.LAST_OPEN_DATE, 'DD/MM/YYYY'), ' ')
WITHIN GROUP (ORDER BY R.LAST_OPEN_DATE DESC)
FROM MEMBER M, REPORT R
WHERE M.MEMBER_ID = R.MEMBER_ID
AND R.STATUS = 1
AND TRUNC(R.LAST_OPEN_DATE) >= TRUNC(SYSDATE) - 120
GROUP BY M.EMAIL;
EMAIL DATES
-------------------- -------------------------------------------
email#1.com 12/04/2014 11/04/2014 10/04/2014 09/04/2014
email#2.com 12/05/2014 02/04/2014 22/03/2014 21/03/2014
SQL Fiddle.
Which is OK for a text report, but not if this query is feeding into a reporting tool.
First of all, number of columns in a query is determined before hand and can't be adjusted by the data. To overcome that, you might be interested in dynamic query
But, in simple static case, you will need to use PIVOT construction.
As a first step, you will need to assign rows to the columns
select EMAIL, row_number() over (partition by email order by last_date) col
from yourtable
then you add "magic" PIVOT:
<your query>
PIVOT
(
max(last_date)
for col in (1, 2, 3, ..., 240)
)

MDX rather complicated sorting

I can't find out a way, how to sort my query, this is the simple query:
SELECT {[Measures].[IB]}
ON COLUMNS,
{[Dim_Product_Models_new].[PLA].members } *
{[Dim Dates_new].[Date Full].&[2013-02-01]:[Dim Dates_new].[Date Full].&[2014-01-01]}
ON ROWS
FROM [cub_dashboard_spares]
The think is, I would get a result for 6 PLAs combined across 12 months (72 rows in total), however it is sorted alphabetically upon PLA.
What i need, is to sort the PLAs based on a measure in last month (2014-01-01 in this case).
Is there any way to perform this task so that the groupping (PLAs, Dates from 2013-02 to 2013-12) is perserved, but only the order of my PLAs is different. (PLA with highest measure in last month would be first, and so on)
Thank you very much for any kind of help
Just put the sorted set on the rows, using the Order function. The third parameter of this function is DESC if you want to sort within each hierarchy level, but still want to get parents before children (like ALL before the single attribute members), or BDESC if you want to sort across all levels.
SELECT {[Measures].[IB]}
ON COLUMNS,
Order({[Dim_Product_Models_new].[PLA].members },
([Measures].[IB], [Dim Dates_new].[Date Full].&[2014-01-01]),
DESC)
*
{[Dim Dates_new].[Date Full].&[2013-02-01]:[Dim Dates_new].[Date Full].&[2014-01-01]}
ON ROWS
FROM [cub_dashboard_spares]
The order function over a crossjoin should preserve the initial order of the first set so reversing the order of the tuple will do the job:
SELECT
{
[Measures].[IB]
} ON COLUMNS,
order(
{[Dim Dates_new].[Date Full].&[2013-02-01]:[Dim Dates_new].[Date Full].&[2014-01-01]} *
{[Dim_Product_Models_new].[PLA].members } ,
[Measures].[IB],
desc
) ON ROWS
FROM [cub_dashboard_spares]
If you want to preserve the oder of appearance of the column labels, you can use the generate function like in the following example from the AW cube:
SELECT
{[Measures].[Internet Sales Amount]} ON 0
,Generate
(
{[Customer].[Country].&[Australia]:[Customer].[Country].&[United Kingdom]}
,(
Order
(
[Date].[Calendar Year].[Calendar Year].MEMBERS
,(
[Customer].[Country].CurrentMember
,[Measures].[Internet Sales Amount]
)
,DESC
)
,[Customer].[Country].CurrentMember
)
) ON 1
FROM [Adventure Works];
Philip,

Resources