MDX: Combining output for different criteria - ssas

I am new to MDX queries, and working in SSAS. I have two queries that are working suitably, but I want their output combined in a single result. The two queries differ in the cities selected by their where clauses, but they both come from the same cube and use the same measure.
A_to_B: Supplier city A to Consumer city B
SELECT { [Measures].[Quantity - Transactions] } ON COLUMNS,
{ [Tb Product].[Name].[Name].ALLMEMBERS } ON ROWS
FROM [Cube]
WHERE ([Tb Supplier].[City].&[A],
[Tb Consumer].[City].&[B])
B_to_A: Supplier city B to Consumer city A
SELECT { [Measures].[Quantity - Transactions] } ON COLUMNS,
{ [Tb Product].[Name].[Name].ALLMEMBERS } ON ROWS
FROM [Cube]
WHERE ([Tb Supplier].[City].&[B],
[Tb Consumer].[City].&[A])
Is there a way for the output of these queries to be produced side-by-side by Product, like this? In SQL I would have used a FULL OUTER JOIN, but I can't figure out the equivalent in MDX.
| | A_to_B | B_to_A |
| ProductA | 10 | 2 |
| ProductB | 100 | 0 |
| ProductC | 0 | 99 |

Simply move you Where statements to columns:
Select
{[Measures].[Quantity - Transactions]} *
{
([Tb Supplier].[City].&[B], [Tb Consumer].[City].&[A]),
([Tb Supplier].[City].&[A], [Tb Consumer].[City].&[B])
} on 0,
{[Tb Product].[Name].[Name].AllMembers} on 1
From [Cube]
You may create calculated members in order to unite two tuples:
With
Member [Tb Supplier].[City].[B2A] as
Aggregate([Tb Supplier].[City].&[B], [Tb Consumer].[City].&[A])
Member [Tb Supplier].[City].[A2B] as
Aggregate([Tb Supplier].[City].&[A], [Tb Consumer].[City].&[B])
Select
{[Tb Supplier].[City].[A2B],[Tb Supplier].[City].[B2A]} on 0
From [Cube]
Where ([Measures].[Quantity - Transactions])

I tested this script and it throws an exception:
SELECT
{[Measures].[Internet Sales Amount]}
*
{
{[Geography].[Geography].[Country].&[United States] * [Product].[Category].&[1]}
,{[Geography].[Geography].[Country].&[France] * [Product].[Category].&[3]}
} ON 0
,{[Date].[Calendar].[Date].&[20070801]} ON 1
FROM [Adventure Works];
This message:
Query (5, 7) The function expects a tuple set expression for the 1
argument. A string or numeric expression was used.
This is because you need to make either side of the cross-join specifically a set - without extra brackets it does not know this and throws an exception.
So this is the "perfect" version of the script:
SELECT
{[Measures].[Internet Sales Amount]}
*
{
{[Geography].[Geography].[Country].&[United States]} * {[Product].[Category].&[1]}
,{[Geography].[Geography].[Country].&[France]} * {[Product].[Category].&[3]}
} ON 0
,{[Date].[Calendar].[Date].&[20070801]} ON 1
FROM [Adventure Works];
I'd prefer to move the measure to the WHERE clause and also get rid of some of the redundant braces:
SELECT
{
{[Geography].[Geography].[Country].&[United States]}
*
{[Product].[Category].&[1]}
,
{[Geography].[Geography].[Country].&[France]}
*
{[Product].[Category].&[3]}
} ON 0
,[Date].[Calendar].[Date].&[20070801] ON 1
FROM [Adventure Works]
WHERE
[Measures].[Internet Sales Amount];
Translated to your cube:
SELECT
{
{[Tb Supplier].[City].&[B]} * {[Tb Consumer].[City].&[A]}
,
{[Tb Supplier].[City].&[A]} * {[Tb Consumer].[City].&[B]}
} ON 0
,[Tb Product].[Name].[Name].ALLMEMBERS ON 1
FROM [Cube]
WHERE
[Measures].[Quantity - Transactions];

Building on the others, here is one that replaces nulls with zeros in the output.
With
Member [Tb Supplier].[City].[B2A] as
Aggregate([Tb Supplier].[City].&[B], [Tb Consumer].[City].&[A])
Member [Tb Supplier].[City].[A2B] as
Aggregate([Tb Supplier].[City].&[A], [Tb Consumer].[City].&[B])
Member [Measures].[Quantity_Transactions] as
Iif( IsEmpty( [Measures].[Quantity - Transactions] ),
0,
[Measures].[Quantity - Transactions] )
SELECT
{ [Measures].[Quantity_Transactions] } *
{ [Tb Supplier].[City].[B2A],
[Tb Supplier].[City].[A2B] } on COLUMNS
, [Tb Product].[Name].[Name].ALLMEMBERS ON ROWS
FROM [Cube]

Related

I need to filter this MDX result set

I am looking to filter the result set below such that I only show results where Dimension A Value 1 have a count of 1, regardless of the value of count for Dimension A Value 2
Dimension A Value 1 Dimension A Value 2
Entity ID Count Count
11 1
78 1
90 1
101 1
114 1
118 1
125 1
134 1
140 1
161 1
169 1
186 1 2
The filtered set would look like
Dimension A Value 1 Dimension A Value 2
Entity ID Count Count
11 1
78 1
90 1
101 1
118 1
125 1
140 1
161 1
169 1
186 1 2
the mdx is
WITH
SET [~COLUMNS] AS
{[Dimension A].[Dimension A].[Value 1], [Dimension A].[Dimension A].[Value 2]}
SET [~ROWS] AS
{[Entity].[Entity].[Entity ID].Members}
SELECT
NON EMPTY CrossJoin([~COLUMNS], {[Measures].[Count]}) ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [My Cube]
I've been playing around with Filter and NonEmpty but I'm new to MDX and my sql brain is hurting. I suppose this is probably trivial to someone with a lot of MDX under their belt but I'm failing. Be gentle this is my first question
You can try a HAVING clause:
WITH
SET [~COLUMNS] AS
{
[Dimension A].[Dimension A].[Value 1],
[Dimension A].[Dimension A].[Value 2]
}
MEMBER [Measures].[CountValue1] AS //<<<<this is new <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
(
[Measures].[Count],
[Dimension A].[Dimension A].[Value 1]
)
SELECT
NON EMPTY
CrossJoin(
[~COLUMNS]
, {[Measures].[Count]}
) ON COLUMNS,
NON EMPTY
[Entity].[Entity].[Entity ID].MEMBERS
HAVING [Measures].[CountValue1] = 1 //<<CHANGED TO NEW MEASURE
ON ROWS
FROM [My Cube];
If you can use HAVING rather than FILTER you will likely see a performance improvement - particularly if your scripts become more complex:
https://blog.crossjoin.co.uk/2006/01/04/the-having-clause/
Just to be complete the slower FILTER version would be this:
WITH
SET [~COLUMNS] AS
{
[Dimension A].[Dimension A].[Value 1],
[Dimension A].[Dimension A].[Value 2]
}
//>>>>>> following is new >>>>>>>>>>>>>>>>>>>>>
MEMBER [Measures].[CountValueNEW] AS
(
[Measures].[Count],
[Dimension A].[Dimension A].[Value 1]
)
SELECT
NON EMPTY
[~COLUMNS]
*{[Measures].[Count]}
ON 0,
NON EMPTY
FILTER(
[Entity].[Entity].[Entity ID].MEMBERS,
[Measures].[CountValueNEW] = 1
)
ON 1
FROM [My Cube];
Your query should be like
Select ([Dimension A].[AttributeHierarchy1].[AttributeHierarchy1],{[Measures].[Value1],[Measures].[Value2]}) on columns,
filter([Dimension B].[EntityID].[EntityID],[Measures].[Value1]=0)
on rows
from yourcube
However there can be an issue. For example your dimesnion has two values A and B, for a particular row , A,value1 =1 but B, Value1=0, this row will appear as A qulifies for it and B gets carried over.
Edit
Lets take an example I want to see Internet Sales for Bottles and Cages haveing sales more than 150$
My initial query
select
([Product].[Subcategory].[Subcategory],[Measures].[Internet Sales Amount]
)
on columns,
[Customer].[City].[City]
on rows
from
[Adventure Works]
Result
Now modify the query
select
([Product].[Subcategory].[Subcategory],[Measures].[Internet Sales Amount]
)
on columns,
filter
(
[Customer].[City].[City], [Measures].[Internet Sales Amount]>150
)
on rows
from
(select [Product].[Subcategory].&[28] on 0 from [Adventure Works])
Result

selecting max of distinct count when on multiple rows

I have a query that does a distinct count of accounts based on CB score. The problem I have is that an account can switch credit scores pretty often. So I would like to take the max credit score for a particular account.
Here is what I have that is returning higher than I want because of the switching of CB scores.
So for example account 1234 initially has a cb score of 500, then later has a cb score of 550. The account 1234 is now in both cb score ranges, but I only want to count them once.
WITH
MEMBER mem1 AS
Count
(
NonEmpty
(
[AccountID].[Account ID].[Account ID].MEMBERS
,measures.[Transaction Amount]
)
)
SELECT
NON EMPTY
{mem1} ON 0
,[CB Score].[RANGE].[RANGE] ON 1
FROM [CUBE]
WHERE [PROMO].[PROMO].&24
Try this:
WITH
MEMBER mem1 AS
Sum
(
[AccountID].[Account ID].[Account ID].MEMBERS
,IIF(
Not IsEmpty([Measures].[Transaction Amount])
And IsEmpty(Sum({[CB Score].[RANGE].CurrentMember.NextMember : null}, [Measures].[Transaction Amount])),
1,
Null
)
)
SELECT
NON EMPTY
{mem1} ON 0
,[CB Score].[RANGE].[RANGE] ON 1
FROM [CUBE]
WHERE [PROMO].[PROMO].&24
Basically the logic is to count the account if they had a transaction in that slice and if no greater CB Score range has a transaction.

MDX: Filtering repeated

I would like to write an MDX query that only show values when a specific repeated dimesions are repeatid more tha one.
That is my mdx query:
SELECT
NON EMPTY { [Measures].[Value] } ON COLUMNS,
NON EMPTY { ([Dim Result].[Sample Number].[Sample Number].ALLMEMBERS
* [Dim Parameter].[IdParameter].[IdParameter].ALLMEMBERS ) } ON ROWS
FROM [Cube]
the result its like that:
Sample Number IdParameter Value
1 3 5
1 4 6
2 3 2
3 4 0
What i want is to get only repeated sample number values like this:
Sample Number IdParameter Value
1 3 5
1 4 6
And remove the other values that are not repeated
Sample Number IdParameter Value
2 3 2
3 4 0
I donĀ“t know how i can do this, it is posible?
I write the next mdx:
SELECT NON EMPTY {[Measures].[Recuento Fact Result]} ON COLUMNS,
NON EMPTY { [Dim Result].[Sample Number].[Sample Number] } ON ROWS
FROM ( SELECT ({ [Dim Parameter].[IdParameter].&[420] , [Dim Parameter].[IdParameter].&[20] } ) ON COLUMNS
FROM [cube])
the result is that:
Sample Number Recount Fact Result
1 1
2 2
3 2
4 1
What i want is get only the Sample number where the recount is biger than 1
thanks for your help
My approach would be to create a calculated member which would hold number of distinct non-empty members per SampleNumber. Then filter out those tuples where the above count is not greater than 1.
UNTESTED (not near my system)
WITH MEMBER [Measures].CntParametersPerSampleNumber as
DistinctCount(
NonEmpty(
[Dim Parameter].[IdParameter].CHILDREN,
([Dim Result].[Sample Number].CURRENTMEMBER, [Measures].[Value])
)
)
SELECT
NON EMPTY { [Measures].[Value] } ON COLUMNS,
NON EMPTY {
Filter(
(
[Dim Result].[Sample Number].[Sample Number].ALLMEMBERS
*
[Dim Parameter].[IdParameter].[IdParameter].ALLMEMBERS
), [Measures].CntParametersPerSampleNumber>1
)
} ON ROWS
FROM [Cube]
You seem to have two separate questions.
The second question can be answered using the HAVING clause:
SELECT
NON EMPTY
{[Measures].[Recuento Fact Result]} ON 0,
NON EMPTY
{[Dim Result].[Sample Number].[Sample Number]}
HAVING [Measures].[Recuento Fact Result] > 1
ON 1
FROM
(
SELECT
({
[Dim Parameter].[IdParameter].&[420]
, [Dim Parameter].[IdParameter].&[20]
}) ON 0
FROM [cube]
)
For you first question you should be able to use the Filter function iteratively to detect if sample number is repeated:
WITH
SET [OrderedSampleNums] AS
Order(
[Dim Result].[Sample Number].[Sample Number].ALLMEMBERS ,
[Dim Result].[Sample Number].CurrentMember.Caption,
BASC
)
SET [RepeatedSampleNums] AS
Filter(
OrderedSampleNums ,
OrderedSampleNums.Item(
OrderedSampleNums.CurrentOrdinal-1
).Caption = [Dim Result].[Sample Number].CurrentMember.Caption
)
SET [NonRepeatedSampleNums] AS
Except(
OrderedSampleNums
,RepeatedSampleNums
)
SELECT
NON EMPTY
{[Measures].[Value]} ON 0,
NON EMPTY
[NonRepeatedSampleNums]
*
[Dim Parameter].[IdParameter].[IdParameter].ALLMEMBERS
ON 1
FROM [Cube];
Not tested but I could try to prototype something against the AdvWorks cube to explore further?

MDX query with TOPCOUNT between date range returning a top with some empty values

I'm using a MDX query to get the top 10 liked Products between two dates. Oddly, the result is composed by some Products with likes within that date range, and some without any likes at all. Here is the query:
SELECT
{ [Measures].[Likes] } ON COLUMNS,
{ TOPCOUNT([Products].[Name].Members, 10, [Measures].[Likes]) } ON ROWS
FROM [Likes]
WHERE ( [Date].[2014].[3].[29]:[Date].[2014].[4].[5] )
Here are the results:
[Measures].[Likes]
[Product].[XX]
[Product].[XX]
[Product].[XX] 139
[Product].[XX]
[Product].[XX] 1
[Product].[XX]
[Product].[XX]
[Product].[XX] 125
[Product].[XX] 111
[Product].[XX] 1
If I change the top limit to 20, for example, the results will have more products with likes but also with more empty ones, and not ordered (like a top usually is).
Using NON EMPTY makes the query return only 5 results instead of 10, and still not ordered.
Thanks!
Try this query
SELECT
{ [Measures].[Likes] } ON 0,
Head(ORDER([Products].[Name].Members,[Measures].[Likes], DESC), 10) ON 1
FROM [Likes]
WHERE ( [Date].[2014].[3].[29]:[Date].[2014].[4].[5] )

Access DB do line item calculation on total of a column before knowing the total

[ edit ]
For the record, here's the problem portion of the query, which is now working:
SELECT
m.groupNum,
t.ea,
( t.ea - ( t.ea * m.margin )) AS estCost,
(( t.ea - ( t.ea * m.margin )) * t.quantity ) AS salesSub,
((( t.ea - ( t.ea * m.margin )) * t.quantity ) /
(
SELECT SUM(( t2.ea - ( t2.ea * m.margin )) * t2.quantity )
FROM temp as t2
INNER JOIN masters as m2
ON t2.mod = m2.mod
WHERE m2.groupNum = m.groupNum
)
) AS salesPercent
etc...
[ end edit ]
I think I need a query that can recursively update itself based on the total of a column's values after inserting values on all the rest of the records for a given (groupNum) range.
I already have the estCost and salesSub fields. Now I need to do calculations on the salesPercent field, which involves knowing the total amount of all salesSub records in a given set (groupNum).
salesPercent =
( salesSub / [the sum total of all salesSub amounts for each groupNum] )
(snip)
SELECT
m.id,
t.priceEach,
( t.priceEach - ( t.priceEach * m.margin )) AS estCost,
(( t.priceEach - ( t.priceEach * m.margin )) * t.quantity ) AS salesSub
-- is it possible to perform calculation on salesPercent here?
INTO output
FROM financeMasters AS m
INNER JOIN temp AS t .......
(end snip)
I have this...
------
output
---------------------------------------------------------------
id | groupNum | priceEach | estCost | salesSub | salesPercent |
---------------------------------------------------------------
1 | apple | 150.00 | 90.00 | 90.00 |
2 | apple | 100.00 | 60.00 | 60.00 |
3 | apple | 50.00 | 30.00 | 30.00 |
but how can I calculate salesPercent on the salesSub total (in this case 180.00) before knowing the total?
You'll probably have to use a subselect to do the math on each row, there's no way to "go back" and change previous rows.
Something like this:
SELECT
m.id,
t.priceEach,
( t.priceEach - ( t.priceEach * m.margin )) AS estCost,
(( t.priceEach - ( t.priceEach * m.margin )) * t.quantity ) AS salesSub,
((( t.priceEach - ( t.priceEach * m.margin )) * t.quantity )
/ (SELECT SUM(( t2.priceEach - ( t2.priceEach * m.margin )) * t2.quantity )
FROM financeMasters AS m2
INNER JOIN temp AS t2 .....
WHERE m2.groupNum = m.groupNum))
AS salesPercent
INTO output
FROM financeMasters AS m
INNER JOIN temp AS t .......
Yes, it's pretty ugly. I had to leave out some details due to not knowing how you were doing the join, and also I'm not sure which table that groupNum is coming from, you didn't show that anywhere.
Like Chad Birch said, a subselect is probably your best bet. You could make it less ugly by storing the subselect in a separate query.
What you are going to do is call that query from another query (and join that with the original). Assuming your query is called salesCalc. It looks like this (I've left out the join):
SELECT m.id,
t.priceEach,
( t.priceEach - ( t.priceEach * m.margin )) AS estCost,
(( t.priceEach - ( t.priceEach * m.margin )) * t.quantity ) AS salesSub
INTO output
FROM financeMasters AS m
INNER JOIN temp AS t .......
You will create another query called salesSum. It uses the first query. It will probably look exactly like this:
select sum(salesSub) as salesGroupTotal, groupNum
from salesCalc
group by groupNum;
You will create a third query called salesPercent. It uses both the first and second. It will look like this (I've left out the join here as well):
select salesCalc.*, salesCalc.SalesSub/salesSum.salesGroupTotal
from salesCalc, salesSum
inner join ...
(YMMV)