PowerBI Cumulative Total by Month within Year (Financial Year) - cumulative-sum

I want to plot cumulative totals (Actual vs Plan) by month within each year (Actually financial year starting in each July). I have the basic cumulative total working across all months- See Figure 1.
But I am struggling to implement a filter that allows users to view the cumulative total for each financial year separately (i.e. with the year starting with a cumulative Actuals and Plans values being zero).
At the moment - if I select the second financial year with the slicer, the cumulative total doesn't start at zero (See figure 2)
Can anyone suggest a fix? I am not sure if the solution is linked to the Filter/ Slicer behavior or the DAX calculation.
Figure 1 : Cumulative totals by month across all years
Figure 2 : Cumulative totals for second Year. Issue as year doesn't start off with Plans and Actuals being zero
My current cumulative total calculations (for Actuals & Plan) are here
Cumulative Actuals =
IF (
MIN ( 'Date Dim'[BOM] )
<= CALCULATE ( MAX ( 'Detailed Breakdown'[Actuals_BOMS]), ALL ('Detailed Breakdown') ),
CALCULATE (
SUM ( 'Detailed Breakdown'[Actual($)]),
FILTER (
ALL ( 'Date Dim'[BOM] ), 'Date Dim'[BOM] <= MAX ( 'Date Dim'[BOM] )
)
))
Cumulative Plan =
CALCULATE (
SUM ( 'Detailed Breakdown'[Plan($)]),
FILTER (
ALL ( 'Date Dim'[BOM] ), 'Date Dim'[BOM] <= MAX ( 'Date Dim'[BOM] )
)
)
A copy of my PBI file is located here

Related

SQL server: smoothed average of day of year

I have a subquery with avg reservoir inflow pr day of year (from 1-365). Now I would like to calculate a smoothed/moving average for each day of year in a new column.
Example: for january 1st (DayOfYear = 1) I would like to calculate a smoothed average of 21 days (10 pre and 10 post days). I.e an avg of days ranging from (356-11). For day of year 55 the avg should be calculated on days of the year ranging from (45-65).
Her is the unfinished query based on a subquery called 'sub' where the 10 years of inflow first are averaged on day of year;
DECLARE #Dager int ;
SET #Dager = 10; /* # days pre and post the actual day of year to be included in avg */
Select sub.Magasin, sub.DayOfYear, AVG(sub.Inflow) as AvgInflow
FROM (SELECT Date, Magasin, Datepart(dy,Date) as DayOfYear, Value as Inflow
FROM inputtable
WHERE Date >= DATEFROMPARTS(2008,1,1) and Date <= DATEFROMPARTS(2017,12,31)) sub
GROUP By sub.Magasin, sub.DayOfYear
ORDER BY sub.magasin, sub.DayOfYear
Without any sample data, I'm going to suggest this for SQL Server 2012+
(Your SQL looks like SQL Server 2012+)
SELECT
Magasin,
Datepart(dy,Date) AS DayOfYear,
AVG(Inflow) OVER (
PARTITION BY Magasin
ORDER BY YEAR(Date), Datepart(dy,Date)
ROWS BETWEEN 10 PRECEDING AND 10 FOLLOWING)
FROM
inputtable
WHERE
Date >= DATEFROMPARTS(2008,1,1) and Date <= DATEFROMPARTS(2017,12,31))

Compare the dates, compute the difference between them, postgres

I have a date column and a balance column for each user. Every time user makes a transaction, a new row gets added to this table. It could be that the user makes 15 transactions during the day, and no transaction at all during 5 days.
Like this one
date balance
2017-06-01 95.63
2017-06-01 97.13
2017-06-01 72.14
2017-06-06 45.04
2017-06-08 20.04
2017-06-09 10.63
2017-06-09 -29.37
2017-06-09 -51.35
2017-06-13 -107.55
2017-06-13 -101.35
2017-06-15 -157.55
2017-06-16 -159.55
2017-06-17 -161.55
The goal is to select the positive and negative transactions made during the same day, compute their average or min value and to consider it as one transaction.If the next day no transaction has been made, then the amount of the previous day should be used.
it means for each day in a month i should calculate an interest and it the balance has not been updated then the balance of the previous day should be used.
Hypothetically my table should look like
date balance
1/6/2017 72.14
6/2/2017 72.14
6/3/2017 72.14
6/4/2017 72.14
6/5/2017 72.14
6/6/2017 45.04
7/6/2017 45.04
8/6/2017 20.04
9/6/2017 -51.35
10/6/2017 -51.35
11/6/2017 -51.35
12/6/2017 -51.35
13/06/2017 -107.55
14/06/2017 -107.55
15/06/2017 -157.55
16/06/2017 -159.55
17/06/2017 -161.55
i have added those days that were missing and group the days that were duplicate.
Once I have this done, I can select the number of positive balance days, e.g. 8 days, compute the average positive balance, and multiply it by 0.4%.
8*58.8525*0.004=0.23
The same should be done with negative balance. but with a different interest rate number of negative balance days, e.g. 9 multiplied by average negative balance during those days and 8.49%.
9*-99.90555556*0.00849=-0.848
So my expected result is just to have these two columns
Neg Pos
-0.848 0.23
How can I do that it in postgres? The function OVERLAP does not really help since I need to specify the dates.
Besides i do not know how to
loop the days and to see if there is a duplicate.
See which days are missing and use the previous balance for each of these missing days.
please try this.. replace table with your table name
with cte as
(
Select "date" as date
,min(balance) as balance
,lead("date") over(order by "date") next_date
,Coalesce(ABS("date" - lead("date") over(order by "date")),1) date_diff
from table
group by "date"
),
cte2 as
(
Select date_diff*balance as tot_bal , date_diff
from cte
Where balance > 0
),
cte3 as
(
Select date_diff*balance as tot_bal , date_diff
from cte
Where balance < 0
)
Select (sum(cte2.tot_bal) / sum(cte2.date_diff) ) * 0.004 as pos
,(sum(cte3.tot_bal) / sum(cte3.date_diff) ) * 0.00849 as neg
from cte2
,cte3;

ParallelPeriod in MDX Query with CurrentMember

I am trying to predict the number of rooms that my organisation will need to book on the basis of how many students we have enrolled for the busy part of our year.
The first part of this problem is calculating the relative decrease/increase in student numbers in this year vs. last year and using that to extrapolate student numbers for this year.
To this end, I am trying to measure the following:
A running total of enrollments in the weeks of the year leading up to a range of start dates e.g. how many students enrolled in weeks 1,2,3... of 2016 who will be here between October and December for a given year
Calculate the percentage increase/decrease over the previous years in student numbers
I have the following query:
WITH MEMBER [Measures].[Period Growth] AS
(
[Measures].[Enrolments By Week],
ParallelPeriod([Weekly Enrolments Date].[ISO Week Calendar].[ISO Year],
1,
[Weekly Enrolments Date].[ISO Week Calendar].[ISO Week].currentmember)
)
SELECT NON EMPTY { [Measures].[Enrolments By Week], [Measures].[Period Growth] } ON COLUMNS,
NON EMPTY { FILTER([Weekly Enrolments Date].[ISO Week Calendar].[ISO Week].&[201738] :
[Weekly Enrolments Date].[ISO Week Calendar].[ISO Week].&[201752],
Cint([Term Record Creation].[ISO Week Number Of Year].CurrentMember.Member_Key) <= 10 --Cint( STRTOMEMBER(#ToISOWeekNumberOfYear, CONSTRAINED).Member_Key )
OR
Cint([Term Record Creation].[ISO Year].CurrentMember.Member_key) <
Cint([Term Start Date].[ISO Year].CurrentMember.Member_key)) } ON ROWS
FROM [Enrolments]
However, I am getting #Error for the calculated member when I set the member expression in the ParallelPeriod as CurrentMember. If I change the CurrentMember to a value like .&[201642] the error disappears - why is this? How can I get the same week in the previous year as a comparison for each week row of the current year?
In addition, how can I get the percentage change for each week relative to the previous year in the same week, while avoiding divide by 0 errors?
To me this looks like a level:
[Weekly Enrolments Date].[ISO Week Calendar].[ISO Week]
So yes this is valid and there will not be an error:
[Weekly Enrolments Date].[ISO Week Calendar].[ISO Week].&[201642]
But I don't think the CURRENTMEMBER function can be applied to a level expression - if you just shorten to the following it might be happier:
[Weekly Enrolments Date].[ISO Week Calendar].currentmember

Get record based on year in oracle

I am creating a query to give number of days between two days based on year. Actually I have below type of date range
From Date: TO_DATE('01-Jun-2011','dd-MM-yyyy')
To Date: TO_DATE('31-Dec-2013','dd-MM-yyyy')
My Result should be:
Year Number of day
------------------------------
2011 XXX
2012 XXX
2013 XXX
I've tried below query
WITH all_dates AS
(SELECT start_date + LEVEL - 1 AS a_date
FROM
(SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date ,
TO_DATE ('25/06/2013', 'DD/MM/YYYY') AS end_date
FROM dual
)
CONNECT BY LEVEL <= end_date + 1 - start_date
)
SELECT TO_CHAR ( TRUNC (a_date, 'YEAR') , 'YYYY' ) AS YEAR,
COUNT (*) AS num_days
FROM all_dates
WHERE a_date - TRUNC (a_date, 'IW') < 7
GROUP BY TRUNC (a_date, 'YEAR')
ORDER BY TRUNC (a_date, 'YEAR') ;
I got exact output
Year Number of day
------------------------------
2011 286
2012 366
2013 176
My question is if i use connect by then query execution takes long time as i have millions of records in table and hence i don't want to use connect by clause
connect by clause is creating virtual rows against the particular record.
Any help or suggestion would be greatly appreciated.
From your vague expected results I think you want the number of records between those dates, not the number of days; but it's rather unclear. Since you refer to a table in the question I assume you want something related to the table data, not simply days between two dates which wouldn't depend on a table at all. (I have no idea what the connect by clause reference means though). This should give you that, if it is what you want:
select extract(year from date_field), count(*)
from t42
where date_field >= to_date('01-Jun-2011', 'DD-MON-YYYY')
and date_field < to_date('31-Dec-2013') + interval '1' day
group by extract(year from date_field)
order by extract(year from date_field);
The where clause is as you'd expect between two dates; I've assumed there might be times in your date field (i.e. not all at midnight) and that you want to count all records on the last date in your range. Then it's grouping and counting based on the year for each record.
SQL Fiddle.
If you want the number of days that have records within the range, then you can just vary the count slightly:
select extract(year from date_field), count(distinct trunc(date_field))
...
SQL Fiddle.
you can use the below function to reduce the number of virtual rows by considering only the years in between.You can check the SQLFIDDLE to check the performance.
First consider only the number of days between start date and the year end of that year or
End date if it is in same year
Then consider the years in between from next year of start date to the year before the end date year
Finally consider the number of days from start of end date year to end date
Hence instead of iterating for all the days between start date and end date we need to iterate only the years
WITH all_dates AS
(SELECT (TO_CHAR(START_DATE,'yyyy') + LEVEL - 1) YEARS_BETWEEN,start_date,end_date
FROM
(SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date ,
TO_DATE ('25/06/2013', 'DD/MM/YYYY') AS end_date
FROM dual
)
CONNECT BY LEVEL <= (TO_CHAR(end_date,'yyyy')) - (TO_CHAR(start_date,'yyyy')-1)
)
SELECT DECODE(TO_CHAR(END_DATE,'yyyy'),YEARS_BETWEEN,END_DATE
,to_date('31-12-'||years_between,'dd-mm-yyyy'))
- DECODE(TO_CHAR(START_DATE,'yyyy'),YEARS_BETWEEN,START_DATE
,to_date('01-01-'||years_between,'dd-mm-yyyy'))+1,years_between
FROM ALL_DATES;
In Oracle you can perform Addition and Substraction to dates like this...
SELECT
TO_DATE('31-Dec-2013','dd-MM-yyyy') - TO_DATE('01-Jun-2011','dd-MM-yyyy')
DAYS FROM DUAL;
it will return day difference between two dates....
select to_date(2011, 'yyyy'), to_date(2012, 'yyyy'), to_date(2013, 'yyyy')
from dual;
TO_DATE(2011,'Y TO_DATE(2012,'Y TO_DATE(2013,'Y
--------------- --------------- ---------------
01-MAY-11 01-MAY-12 01-MAY-13
select to_char(date_field,'yyyy'), count(*)
from your_table
where date_field between to_date('01-Jun-2011', 'DD-MON-YYYY')
and to_date('31-Dec-2013 23:59:59', 'DD-MON-YYYY hh24:mi:ss')
group by to_char(date_field,'yyyy')
order by to_char(date_field,'yyyy');

Adapting SQL to work over multiple weeks

I have a report that determines employees that have entered less than 40 hours on their timesheets for a specific week:
With Under40s as (
Select Distinct Entry.UUID, Entry.FirstName, Entry.LastName, Team.PersonUnit, Team.TeamName
From Entry as Entry
Inner Join TeamOrganization as Team on Team.PersonUUID = Entry.UUID and #EnteredDate between Team.PeriodBeginDate and Team.PeriodEndDate
Where
(
Select sum(Entry2.Hours) From Entry as Entry2
Where Entry2.UUID = Entry.UUID and Entry2.Date Between #StartDate and #EndDate
) < 40
Or not exists
(
Select 1 From Entry as Entry2
Where Entry2.UUID = Entry.UUID and Entry2.Date Between #StartDate and #EndDate
)
)
Select distinct Under40s.UUID, Under40s.FirstName, Under40s.LastName, Under40s.PersonUnit, Under40s.TeamName, Case
When Sum(Entry.Hours) is null then 0
Else Sum(Entry.Hours)
End as TotalHours
From Under40s
Left Join Entry as Entry on Entry.UUID = Under40s.UUID and Entry.Date Between #StartDate and #EndDate
Where Under40s.PersonUnit in (#PersonUnit) and Under40s.TeamName in (#Teams)
Group By Under40s.UUID, Under40s.FirstName, Under40s.LastName, Under40s.PersonUnit, Under40s.TeamName
Order By Under40s.LastName, Under40s.FirstName
The client now wants to be able to enter a date range of more than a week. I'm thinking they want to see for each week within that date range, who's reporting less than 40 hours. I am not sure how, or if, I can modify the SQL to deliver such a report. Can anyone give me a hint?
Thanks!
Assuming every working day is 8 hours, one option would be to get the the day differences between start date and end date and then multiplying that by 8 and using the product as the criteria. For example: startdate: 20/11/2013 and enddate: 31/12/2013 is 41 days including weekends (so you also you have to filter weekends or non-working days), but just as example 41*8 = 328. So instead of less than 40 it will be less than 328. Here's a link to a working solution of excluding weekends and other specified dates: Number of days in date range, excluding weekends and other dates, in C#

Resources