DAX partition/monthly totals - powerpivot

I am trying to figure out how to mimic a SQL partition in a DAX query. If I was using SQL I would use something similar to this:
sum([Total Units]) over (partition by [Fiscal Month]) as ttl_mth_unit
or
,Sum(Case when 'Order Line Item Details'[No of Transfers] = 1 then 'Order Line Item Details'[Total Units] end)) as single
,Single/ sum('Order Line Item Details'[Total Units]) as perct_single
My data currently looks like this:
Fiscal Month Transfer Cnt Units
2017-Apr 0 100
2017-Apr 1 300
Ideally the results would look like this:
Fiscal Month 0transfer 1transfer %0 %1 ttl
2017-Apr 100 300 .25 .75 400
or this:
Fiscal Mon th Transfer Cnt Units % ttl units
2017-Apr 0 100 0.25 400
2017-Apr 1 300 0.75 400
this is my DAX code
evaluate(
filter(
addcolumns(
summarize(
'Order Line Details'
,'Calendar'[Fiscal Month]
,'Calendar'[Fiscal Year Nbr]
,'Order Line Item Details'[No of Transfers]
,"Total Units Test",'Order Line Item Details'[Total Units]
),
"Month Abbr", Mid('Calendar'[Fiscal Month],1,3)
,"Month ID", 'Calendar'[Fiscal Year Nbr]&"-"&Mid('Calendar'[Fiscal Month],1,3)
),
[Fiscal Year Nbr]>(2015)
)
)
I've tried using summarize and different variations of sumx but I am either using the wrong functions or not setting it up properly.

CALCULATE([Total],DATESMTD(Date[calendarDate]))
Do not forget to mark your date table as date table in Tabular model, otherwise this will not wok. It got me before.

Related

How to calculate a percentage on different values from same column with different criteria

I'm trying to write a query in SSRS (using SQL) to calculate an income statement percentage of sales for each month (the year is a parameter chosen by the user at runtime). However, the table I have to use for the data lists all of the years, months, accounts, dollars, etc together and looks like this:
ACCT_YEAR
ACCT_PERIOD
ACCOUNT_ID
CREDIT_AMOUNT
2021
1
4000
20000
2021
2
4000
25000
2021
1
5000
5000
2021
2
5000
7500
2021
1
6000
4000
2021
2
6000
8000
etc, etc (ACCOUNT_ID =4000 happens to be the sales account)
As an example,
I need to calculate
CREDIT_AMOUNT when ACCT_YEAR = 2021, ACCT_PERIOD=1, and ACCOUNT_ID=5000
/
CREDIT_AMOUNT when ACCT_YEAR = 2021, ACCT_PERIOD=1, and ACCOUNT_ID=4000
* 100
I would then do that for each ACCT_PERIOD in the ACCT_YEAR.
Hope that makes sense...What I want would look like this:
ACCT_YEAR
ACCT_PERIOD
ACCOUNT_ID
PERCENTAGE
2021
1
5000
0.25
2021
2
5000
0.30
2021
1
6000
0.20
2021
2
6000
0.32
I'm trying to create a graph that shows the percentage of sales of roughly 10 different accounts (I know their specific account_ID's and will filter by those ID's) and use the line chart widget to show the trends by month.
I've tried CASE scenarios, OVER scenarios, and nested subqueries. I feel like this should be simple but I'm being hardheaded and not seeing the obvious solution.
Any suggestions?
Thank you!
One important behaviour to note is that window functions are applied after the where clause.
Because you need the window functions to be applied before any where clause (which would filter account 4000 out), they need to be used in one scope, and the where clause in another scope.
WITH
perc AS
(
SELECT
*,
credit_amount * 100.0
/
SUM(
CASE WHEN account_id = 4000 THEN credit_amount END
)
OVER (
PARTITION BY acct_year, accr_period
)
AS credit_percentage
FROM
your_table
)
SELECT
*
FROM
perc
WHERE
account_id IN (5000,6000)
You just to use a matrix with a parent column group for ACCT_YEAR and a child column group for ACCT_PERIOD. Then you can use your calculation. If you format the textbox for percentage, you won't need to multiply it by 100.
Textbox value: =IIF(ACCOUNT_ID=4000, Sum(CREDIT_AMOUNT), 0) = 0, 0, IIF(ACCOUNT_ID=5000, Sum(CREDIT_AMOUNT), 0) / IIF(ACCOUNT_ID=4000, Sum(CREDIT_AMOUNT), 0)

TABLEAU CALCULATED FIELD OPTIMIZATION

I have a calculated field called FirstSale where I observe the first instance of a product that sells more than 80% of its inventory.
I look at the product ID and the timestamp (converted to string) and the % of inventory sold.
How can this query be optimized to not depend on this many fields OR how can this be converted to a SQL query.
Calculated Field Logic:
IF STR[SaleDate]) =
{FIXED [ID], {FIXED [ID], STR([SaleDate]), [Inventory %]:
IF MIN([Inventory %]) > 0.8
THEN 1 ELSE 0 END}: MIN([STR(SaleDate]))}
THEN 1
END
The data looks like this
Where there is a product ID, Sale Date, Inventory % and the last column (with 1s and 0s) is the calculated field.
Essentially, The goal is that the calculation should return 1 only for the first time a an ID shows Inventory % > 80%. In all other cases, return 0.
For example looking at the second ID, the only value combination that should have a 1 is October 28 (2020083008056, October 28 2020, 84.00%, 1 ) and all other values should return 0.
So the full return for the second ID would be
(2020083008056, October 28 2020, 84.00%, 1 )
(2020083008056, October 29 2020, 84.36%, 0 )
(2020083008056, October 30 2020, 84.67%, 0 )
(2020083008056, October 31 2020, 84.67%, 0 )
I have recreated some sample data to solve your problem. If I have not misunderstood your LOD calculation should be pretty much simpler. Let's have a look.
Sample data re-created
Added one CF just to check whether inventory sale is greater or equal to .80 and added it to view to create a data like you have shown.
Now add your desired field with calculation as
{FIXED [Prod id] : MIN(
IF [Greater than 80]=1 then [Date] end)} = [Date]
Adding this field to view/filter should serve your purpose. See it
Still if you want returns as 0 or 1 use this calculation instead
IF {FIXED [Prod id] : MIN(
IF [Greater than 80]=1 then [Date] end)} = [Date]
then 1 else 0 end

How do I build a MDX query that considers only facts that happened in the last 10 days of February?

I have a fact table that has a time dimension, which contains year, month, day and hour.
I was able to find ways to filter things that happened in a given day, or month (simple where/filter by the desired level). But I would like to create an MDX query that filter the results so my cube has information about the facts recorded in the last 10 days of febraury.
Is there anyway I can do it?
Assuming you have all the days of February in your cube, you could use a set inside there WHERE clause.
Something like this..
WHERE ([Date].Month)
Supposing you have a Year-Month-Day-Hour hierarchy in place and there may be some dates missing
Select....... on COLUMN,
....... ON ROWS
FROM ....
WHERE
({[Time].[Month].&[Feb 2015].LastChild.LAG(10) : [Date].[Month].&[Feb 2015].LastChild})
If no dates are missing in the date dim,
select ... ON COLUMNS,
... ON ROWS
FROM ...
WHERE
({[Time].[Date].&[02/19/2015] : [Date].[Date].&[02/28/2015]})
If you want the sales for last 10 days of Feb for every year:
SELECT Measures.Sales ON COLUMNS,
Products.Products.MEMBERS ON ROWS
FROM
(
SELECT
generate //This would build the set for the last 10 days of Feb for every year
(
[Time].[Year].[All].children,
TAIL //This returns the last 10 days of february(second month)
(
[Time].[Year].CURRENTMEMBER.FIRSTCHILD.LEAD(1).CHILDREN,
10
)
) ON COLUMNS
FROM YourCube
)
Just as some extra info - if you want a "rolling" 10 day sum or 10 day average then code similar to the following is a possible approach:
WITH
MEMBER [Measures].[Sum 10] AS
Sum
(
LastPeriods
(10
,[Date].[Calendar].CurrentMember
)
,[Measures].[Internet Order Count]
)
MEMBER [Measures].[MovAvg 10] AS
Avg
(
LastPeriods
(10
,[Date].[Date].CurrentMember
)
,[Measures].[Internet Order Count]
), format_string = "#.000"
SELECT
{
[Measures].[Internet Order Count]
,[Measures].[Sum 10]
,[Measures].[MovAvg 10]
} ON 0
,Descendants
(
[Date].[Calendar].[Month].&[2006]&[2]
,[Date].[Calendar].[Date]
) ON 1
FROM [Adventure Works];
It returns data like the following:

Group By with Case statement?

I need find the number Sum of orders over a 3 day range. so imagine a table like this
Order Date
300 1/5/2015
200 1/6/2015
150 1/7/2015
250 1/5/2015
400 1/4/2015
350 1/3/2015
50 1/2/2015
100 1/8/2015
So I want to create a Group by Clause that Groups anything with a date that has the same Month, Year and a Day from 1-3 or 4-6, 7-9 and so on until I reach 30 days.
It seems like what I would want to do is create a case for the grouping that includes a loop of some type but I am not sure if this is the best way or if it is even possible to combine them.
An alternative might be create a case statement that creates a new column that assigns group number and then grouping by that number, month, and Year.
Unfortunately I've never used a case statement so I am not sure which method is best or how to execute them especially with a loop.
EDIT: I am using Access so it looks like I will be using IIF instead of Case
Consider the Partition Function and a crosstab, so, for example:
TRANSFORM Sum(Calendar.Order) AS SumOfOrder
SELECT Month([CalDate]) AS TheMonth, Partition(Day([Caldate]),1,31,3) AS DayGroup
FROM Calendar
GROUP BY Month([CalDate]), Partition(Day([Caldate]),1,31,3)
PIVOT Year([CalDate]);
As an aside, I hope you have not named a field / column as Date.
How about the following:
COUNT OF ORDERS
select year([Date]) as yr,
month([Date]) as monthofyr,
sum(iif((day([Date])>=1) and (day([Date])<=3),1,0)) as days1to3,
sum(iif((day([Date])>=4) and (day([Date])<=6),1,0)) as days4to6,
sum(iif((day([Date])>=7) and (day([Date])<=9),1,0)) as days7to9,
sum(iif((day([Date])>=10) and (day([Date])<=12),1,0)) as days10to12,
sum(iif((day([Date])>=13) and (day([Date])<=15),1,0)) as days13to15,
sum(iif((day([Date])>=16) and (day([Date])<=18),1,0)) as days16to18,
sum(iif((day([Date])>=19) and (day([Date])<=21),1,0)) as days19to21,
sum(iif((day([Date])>=22) and (day([Date])<=24),1,0)) as days22to24,
sum(iif((day([Date])>=25) and (day([Date])<=27),1,0)) as days25to27,
sum(iif((day([Date])>=28) and (day([Date])<=31),1,0)) as days28to31
from tbl
where [Date] between x and y
group by year([Date]),
month([Date])
Replace x and y with your date range.
The last group is days 28 to 31 of the month, so it may contain 4 days' worth of orders, for months that have 31 days.
THE ABOVE IS A COUNT OF ORDERS.
If you want the SUM of the order amounts:
SUM OF ORDER AMOUNTS
select year([Date]) as yr,
month([Date]) as monthofyr,
sum(iif((day([Date])>=1) and (day([Date])<=3),order,0)) as days1to3,
sum(iif((day([Date])>=4) and (day([Date])<=6),order,0)) as days4to6,
sum(iif((day([Date])>=7) and (day([Date])<=9),order,0)) as days7to9,
sum(iif((day([Date])>=10) and (day([Date])<=12),order,0)) as days10to12,
sum(iif((day([Date])>=13) and (day([Date])<=15),order,0)) as days13to15,
sum(iif((day([Date])>=16) and (day([Date])<=18),order,0)) as days16to18,
sum(iif((day([Date])>=19) and (day([Date])<=21),order,0)) as days19to21,
sum(iif((day([Date])>=22) and (day([Date])<=24),order,0)) as days22to24,
sum(iif((day([Date])>=25) and (day([Date])<=27),order,0)) as days25to27,
sum(iif((day([Date])>=28) and (day([Date])<=31),order,0)) as days28to31
from tbl
where [Date] between x and y
group by year([Date]),
month([Date])

MDX -No Sales over 30 days

I'd like to get the product with zero sales over 30 days. E.g. Below is my expected result:
Store,Product,Days
Store1, product1, 33
Store1, product2, 100
Store2, product5, 96
Store34, product14, 78
Store100, product9, 47
So I wrote below query:
WITH
MEMBER [Measures].[Zero Sales Days]
AS
COUNT(
FILTER(
NONEMPTY( [Calendar].[Date].[Day],[Measures].[POS Qty])
, ( [Measures].[POS Qty]=0)
)
)
SELECT
([Store].[Store].[Store],[product].[product].[product]) on 1,
([MEASURES].[Zero Sales Days]) ON 0
FROM [testcube]
The problem is: How to filter the case: days of zero sales<30
Thanks,
Nia
I did some change and then ran against my DB.
I got nothing if I added the where cause. If not, the result is '#Error'.
I need not select any time related dimension. What I want to do for the report is: select store and product dimension, and define a calculated measure to get the count. Boyan, I will be really appreciated it if you can need the detailed the query for it.
The function LastPeriods is what you're looking for:
WITH
MEMBER [Measures].[Zero Sales Days]
AS COUNT(
FILTER([Calendar].[Date].[Day],
SUM( LastPeriods(30, [Calendar].[Date].currentmember),[Measures].[POS Qty])
= 0 )
)
SELECT
([Store].[Store].[Store],[product].[product].[product]) on 1,
([MEASURES].[Zero Sales Days]) ON 0
FROM [testcube]
The following query works against Adventure Works and shows you the products with no sales for over 30 days from the date in the WHERE clause back:
WITH
MEMBER [Measures].[Number of Periods With No Sales] AS
Iif(([Date].[Date].CurrentMember, [Measures].[Internet Sales Amount])=0,
([Date].[Date].PrevMember, [Measures].[Number of Periods With No Sales])+1,
NULL
)
MEMBER [Measures].[Number of > 30 Periods With No Sales] AS
Sum(
Iif([Measures].[Number of Periods With No Sales] > 30,
[Measures].[Number of Periods With No Sales],
NULL
)
)
SELECT
{
[Measures].[Number of > 30 Periods With No Sales]
} ON 0,
NON EMPTY {
[Product].[Product Categories].[Product]
} ON 1
FROM [Adventure Works]
WHERE [Date].[Calendar].[Date].&[860]
You will need to re-work it (change the dimension/measure names) to get it to work against your db. Please let me know if you need a query which can give you all products regardless of the date, which have at least one period with more than 30 days with no sales (e.g. max period with no sales, or an arbitrary such period). This will require a few changes. Also, since the query is using recursion it may be slow - if it is too slow we can see how to improve its performance - something which may require changes to your data model to support this bit of analytics.