Sum amount base from previous value on slicer power bi - sql-server

I have a table like this:
Year Month Code Amount
---------------------------------------
2017 11 a 7368
2017 11 b 3542
2017 12 a 4552
2017 12 b 7541
2018 1 a 6352
2018 1 b 8376
2018 2 a 1287
2018 2 b 3625
I make slicer base on Year and Month (ignore the Code), and I want to show SUM of Amount like this :
If I select on slicer Year 2017 and Month 12, the value to be shown is SUM Amount base on 2017-11, and select on slicer Year 2018 and Month 1 should be SUM Amount base on 2017-12
I have tried this one for testing with, but this not allowed:
Last Month = CALCULATE(SUM(Table[Amount]); Table[Month] = SELECTEDVALUE(Table[Month]) - 1)
How to do it right?
I want something like this
NB: I use direct query to SQL Server
Update: At this far, I added Last_Amount column in SQL Server Table by sub-query, maybe you guys have a better way for my issue

The filters in a CALCULATE statement are only designed to take simple statements that don't have further calculations involved. There are a couple of possible remedies.
1. Use a variable to compute the previous month number before you use it in the CALCULATE function.
Last Month =
VAR PrevMonth = SELECTEDVALUE(Table[Month]) - 1
RETURN CALCULATE(SUM(Table[Amount]), Table[Month] = PrevMonth)
2. Use a FILTER() function. This is an iterator that allows more complex filtering.
Last Month = CALCULATE(SUM(Table[Amount]),
FILTER(ALL(Table),
Table[Month] = SELECTEDVALUE(Table[Month]) - 1))
Edit: Since you are using year and month, you need to have a special case for January.
Last Month =
VAR MonthFilter = MOD(SELECTEDVALUE(Table[Month]) - 2, 12) + 1
VAR YearFilter = IF(PrevMonth = 12,
SELECTEDVALUE(Table[Year]) - 1,
SELECTEDVALUE(Table[Year]))
RETURN CALCULATE(SUM(Table[Amount]),
Table[Month] = MonthFilter,
Table[Year] = YearFilter)

Related

Create array of dates/times between set hours

I'm trying to create a horizontal, ascending ARRAY of date/times but only including times that I specify (8:00AM-5:00PM). I've tried using NETWORKDAYS.INTL, but that seemed to be more difficult and inevitably, did not render accurate results the way I was doing it (probably incorrectly).
February 1, 2020 8:00AM
February 1, 2020 9:00AM
[all hours in between]
February 1, 2020 4:00PM
February 1, 2020 5:00PM
February 2, 2020 8:00AM
[...]
February 2, 2020 5:00PM
February 3, 2020 8:00AM
I'm able to do the formula for the first day, but I'm stumbling at adding the following days.
Here is what I have at the moment:
=IF(
AND(J2>=$E1+$B1,J2<$E1+$C1),
J2+(1/24),
J2-$C1+$B1
)
J2 = The first date in the array =EOMONTH(TODAY(),-1)+1+[8:00AM]
E1 = First day of the month =EOMONTH(TODAY(),-1)+1
B1 = Start time 8:00AM
C1 = End time 5:00PM
Here is my sheet. My formula is in K2 (currently dragging the formula across the row, but a single formula would be ideal.)
EDIT:
After playing with it and discovering ROUNDDOWN to extract the date, I came up with this:
=IF(
AND(J2>=ROUNDDOWN(J2,0)+$B1,J2<ROUNDDOWN(J2,0)+$C1),
J2+(1/24),
ROUNDDOWN(J2,0)+1+$B1
)
I think this will work based on some other discussions we had. it is an automatically generated array that will show just the date for the past 2 weeks. then the date and time for the upcoming two weeks, then just the date for 2 weeks after that. You'll see the number 42 in the SEQUENCE portion of the formula. this controls the overall number of days that the formula works for. You'll also see TODAY()-14. This is when the array is set to "start".
=ARRAYFORMULA(TRANSPOSE(TODAY()-14+QUERY(SEQUENCE(42*24,1,0)/24,"where (Col1 % 1=0 and Col1<14) or (Col1 % 1=0 and Col1>=28) or (Col1 % 1<="&N(C1+0.000001)&" and Col1 % 1>="&N(B1)&" and Col1>=14 and Col1<29)",0)))
try:
=ARRAYFORMULA(E1-1+
SPLIT(QUERY(TRANSPOSE(REPT(TRANSPOSE(ROW(INDIRECT("A1:A"&
DAYS(EOMONTH(E1, 3), E1))))&" ",
HOUR(C1)-HOUR(B1)+1)),,9^9), " ")+
SPLIT(REPT(QUERY((ROW(INDIRECT("A1:A"&
HOUR(C1)-HOUR(B1)+1))+HOUR(B1)-1)/24&" ",,9^9),
DAYS(EOMONTH(E1, 3), E1)), " "))
to change span from 3 months to one replace those two 3 for two 1
shorter, faster, lightweight mathematical solution:
=ARRAYFORMULA(SPLIT(QUERY(TRANSPOSE(QUERY(TRANSPOSE(ARRAY_CONSTRAIN(
N(E1)+SEQUENCE(DAYS(EOMONTH(E1, D1-1), E1), 24, HOUR(B1))/24,9^9,
1+HOUR(C1)-HOUR(B1))),,9^9)),,9^9), " "))
While this does solve my overall problem, it's not the ultimate formula I'm seeking which would be a single ARRAYFORMULA.
=IF(
AND(J2>=ROUNDDOWN(J2,0)+$B1,J2<ROUNDDOWN(J2,0)+$C1),
J2+(1/24),
ROUNDDOWN(J2,0)+1+$B1
)
#MattKing solution:
=ARRAYFORMULA(TRANSPOSE(EOMONTH(TODAY(),-1)+1+QUERY(MOD(
SEQUENCE(SUM({-1,1}*EOMONTH(TODAY(),{-1,D1}))*24,1,0)/24,{9^99,1}),
"select Col1 where Col2<="&N(C1+0.000001)&" and Col2>="&N(B1),0)))

Compare two dates and get difference in exact days, months, years, and more SQL Server 2008 R2

I am comparing two dates in SQL Server and need following information in precise manner:
Input:
Start Date = 12/28/2015
End Date = 12/25/2020
Result returned (each piece in different column):
Days = 1825 (includes start/end date in calculation)
Years = 4
Months = 11
Days = 28
How many days in Start Month = 31 days (Because it is December)
Occupancy in Start Month = (because December has 31 days, it would be 4/31) = .12903225806
How many days in End Month = 31 days (Because it is December)
Occupancy in End Month = (because December has 31 days, it would be 25/31) = .8064516129
For 1-3, you can check https://www.w3schools.com/sql/func_sqlserver_datediff.asp. It's fairly simple, you can just use datediff and specify the unit of interest.
For 4, you can simply do day(Start Date) to get the date.
For 5 and 7, you can use:
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#myDate)+1,0)
to get the end of month date of the given date and use day() to get the day part of the datetime object.
For 6 and 8, it's just simple math, you get number of month in that month from 5 and 7, and then, if Start Date, then (day(eom(Start Date)) - day(Start Date) + 1) / day(eom(date) and if End Date, then day(End Date) / day(eom(End Date)).
I didn't give you the full code as I don't have SQL server running besides me. Hope it helps.

How to change from annual year end to annual mid year in SAS

I currently work in SAS and utilise arrays in this way:
Data Test;
input Payment2018-Payment2021;
datalines;
10 10 10 10
20 20 20 20
30 30 30 30
;
run;
In my opinion this automatically assumes a limit, either the start of the year or the end of the year (Correct me if i'm wrong please)
So, if I wanted to say that this is June data and payments are set to increase every 9 months by 50% I'm looking for a way for my code to recognise that my years go from end of June to the next end of june
For example, if I wanted to say
Data Payment_Pct;
set test;
lastpayrise = "31Jul2018";
array payment:
array Pay_Inc(2018:2021) Pay_Inc: ;
Pay_Inc2018 = 0;
Pay_Inc2019 = 2; /*2 because there are two increments in 2019*/
Pay_Inc2020 = 1;
Pay_Inc2021 = 1;
do I = 2018 to 2021;
if i = year(pay_inc) then payrise(i) * 50% * Pay_Inc(i);
end;
run;
It's all well and good for me to manually do this for one entry but for my uni project, I'll need the algorithm to work these out for themselves and I am currently reading into intck but any help would be appreciated!
P.s. It would be great to have an algorithm that creates the following
Pay_Inc2019 Pay_Inc2020 Pay_Inc2021
1 2 1
OR, it would be great to know how the SAS works in setting the array for 2018:2021 , does it assume end of year or can you set it to mid year or?
Regarding input Payment2018-Payment2021; there is no automatic assumption of yearness or calendaring. The numbers 2018 and 2021 are the bounds for a numbered range list
In a numbered range list, you can begin with any number and end with any number as long as you do not violate the rules for user-supplied names and the numbers are consecutive.
The meaning of the numbers 2018 to 2021 is up to the programmer. You state the variables correspond to the June payment in the numbered year.
You would have to iterate a date using 9-month steps and increment a counter based on the year in which the date falls.
Sample code
Dynamically adapts to the variable names that are arrayed.
data _null_;
array payments payment2018-payment2021;
array Pay_Incs pay_inc2018-pay_inc2021; * must be same range numbers as payments;
* obtain variable names of first and last element in the payments array;
lower_varname = vname(payments(1));
upper_varname = vname(payments(dim(payments)));
* determine position of the range name numbers in those variable names;
lower_year_position = prxmatch('/\d+\s*$/', lower_varname);
upper_year_position = prxmatch('/\d+\s*$/', upper_varname);
* extract range name numbers from the variable names;
lower_year = input(substr(lower_varname,lower_year_position),12.);
upper_year = input(substr(upper_varname,upper_year_position),12.);
* prepare iteration of a date over the years that should be the name range numbers;
date = mdy(06,01,lower_year); * june 1 of year corresponding to first variable in array;
format date yymmdd10.;
do _n_ = 1 by 1; * repurpose _n_ for an infinite do loop with interior leave;
* increment by 9-months;
date = intnx('month', date, 9);
year = year(date);
if year > upper_year then leave;
* increment counter for year in which iterating date falls within;
Pay_Incs( year - lower_year + 1 ) + 1;
end;
put Pay_Incs(*)=;
run;
Increment counter notes
There is a lot to unpack in this statement
Pay_Incs( year - lower_year + 1 ) + 1;
+ 1 at the end of the statement increments the addressed array element by 1, and is the syntax for the SUM Statement
variable + expression The sum statement is equivalent to using the SUM function and the RETAIN statement, as shown here:
retain variable 0;
variable=sum(variable,expression);
year - lower_year + 1 computes the array base-1 index, 1..N, that addresses the corresponding variable in the named range list pay_inc<lower_year>-pay_inc<upper_year>
Pay_Incs( <computed index> ) selects the variable of the SUM statement
This is a wonderful use case of the intnx() function. intnx() will be your best friend when it comes to aligning dates.
In the traditional calendar, the year starts on 01JAN. In your calendar, the year starts in 01JUN. The difference between these two dates is exactly 6 months. We want to shift our date so that the year starts on 01JUN. This will allow you to take the year part of the date and determine what year you are on in the new calendar.
data want;
format current_cal_year
current_new_year year4.
;
current_cal_year = intnx('year', '01JUN2018'd, 0, 'B');
current_new_year = intnx('year.6', '01JUN2018'd, 1, 'B');
run;
Note that we shifted current_new_year by one year. To illustrate why, let's see what happens if we don't shift it by one year.
data want;
format current_cal_year
current_new_year year4.
;
current_cal_year = intnx('year', '01JUN2018'd, 0, 'B');
current_new_year = intnx('year.6', '01JUN2018'd, 0, 'B');
run;
current_new_year shows 2018, but we really are in 2019. For 5 months out of the year, this value will be correct. From June-December, the year value will be incorrect. By shifting it one year, we will always have the correct year associated with this date value. Look at it with different months of the year and you will see that the year part remains correct throughout time.
data want;
format cal_month date9.
cal_year
new_year year4.
;
do i = 0 to 24;
cal_month = intnx('month', '01JAN2016'd, i, 'B');
cal_year = intnx('year', cal_month, i, 'B');
new_year = intnx('year.6', cal_month, i+1, 'B');
year_not_same = (year(cal_year) NE year(new_year) );
output;
end;
drop i;
run;

Expression to find value for previous year SSRS

I need some help with expressions.I need a report that shows calcul of a field for current year as well as previous year respecting this rule expression(Last-Last Previous)/Last Previous*100. Also the report runs on a Year Parameter.
Below is an example for the result
for example i selected the years 2010 2011 2012
year
Data 2010 2011 2012
hp 14 25 30
Dell 17 18 20
and the result i want
year
Data 2010 2011 2012 2011/2012
hp 14 25 30 0.002 (Last -Last Previous)/(last Previous*100) =(30-25)/(25*100)
Dell 17 18 20 0.0040
How can i do this
SELECT
NON EMPTY
{[Measures].[Val]} ON COLUMNS
,NON EMPTY
{
[DimCAT].[lbl].[lbl].ALLMEMBERS * [DimDate].[Year].[Year].ALLMEMBERS
} ON ROWS
FROM [Data];
Via mdx you need to use a WITH clause.
The calculated member that is added is called 2011/12 but is actually the last member of the year hierarchy compared to the previous year - so maybe not exactly what you require:
WITH
MEMBER [DimDate].[Year].[2011/12] AS
(
Tail([DimDate].[Year].[Year].ALLMEMBERS).Item(0).Item(0)
-
Tail([DimDate].[Year].[Year].ALLMEMBERS).Item(0).Item(0).PrevMember
)
/
Tail([DimDate].[Year].[Year].ALLMEMBERS).Item(0).Item(0).PrevMember
* 100
SELECT
NON EMPTY
{
[DimDate].[Year].[Year].ALLMEMBERS
,[DimDate].[Year].[2011/12]
} ON COLUMNS
,NON EMPTY
{[DimCAT].[lbl].[lbl].ALLMEMBERS} ON ROWS
FROM [Data]
WHERE
[Measures].[Val];
Using the strToSet function you could get a single member set using the parameter:
WITH
SET [s] AS
StrToSet('{[DimDate].[Year].[Year].[' + #Yr + ']}')
MEMBER [DimDate].[Year].[LastVsPrev] AS
([s].Item(0).Item(0) - [s].Item(0).Item(0).PrevMember)
/
[s].Item(0).Item(0).PrevMember
* 100
SELECT
NON EMPTY
Union
(
{
[s].Item(0).Item(0).PrevMember.PrevMember : [s].Item(0).Item(0)
}
,{[DimDate].[Year].[LastVsPrev]}
) ON COLUMNS
,NON EMPTY
{[DimCAT].[lbl].[lbl].ALLMEMBERS} ON ROWS
FROM [Data]
WHERE
[Measures].[Val];
If you have the chance to change the source dataset i would bring the dataset with the year UNPIVOTed and with a Row_Number on Data with year descending order. That way you can filter the last two years you want.
(this was tested in SSRS 2008 there might be easier ways especially in later versions)
select
Data,[year],measure,ROW_NUMBER() over (partition by data order by [year] desc) i
from (values
('hp',2010,14),
('hp',2011,25),
('hp',2012,30),
('Dell',2010,17),
('Dell',2011,18),
('Dell',2012,20)) t (data,year,measure)
Data year measure i
hp 2010 14 3
hp 2011 25 2
hp 2012 30 1
Dell 2010 17 3
Dell 2011 18 2
Dell 2012 20 1
And do a Column Group on year and a row group on data.
Expression SUM(Field!Measure.Value) on the intersecting cell of the 2 groups.
Add Column after column group and add the Expression to the Header cell
=MAX(iif(Fields!i.Value = 2,Fields!year.Value.ToString(),""))+"/"+ MAX(iif(Fields!i.Value = 1,Fields!year.Value.ToString(),""))
and the next expression to the Cell
=iif(SUM(iif(Fields!i.Value = 2,Fields!measure.Value,0))=0,0,SUM(iif(Fields!i.Value = 1,Fields!measure.Value,0))-SUM(iif(Fields!i.Value = 2,Fields!measure.Value,0)) / (SUM(iif(Fields!i.Value = 2,Fields!measure.Value,0))*100))
I think the formula is OK, but it's returning different values than what you posted as expected.

Utilizing SQL datepart to indentify consecutive periods of time

I have a stored procedure that works correctly, but don't understand the theory behind why it works. I'm indentifying a consecutive period of time by utilizing a datepart and dense rank (found solution through help elsewhere).
select
c.bom
,h.x
,h.z
,datepart(year, c.bom) * 12 + datepart(month, c.bom) -- this is returning a integer value for the year and month, allowing us to increment the number by one for each month
- dense_rank() over ( partition by h.x order by datepart(year, c.bom) * 12 + datepart(month, c.bom)) as grp -- this row does a dense rank and subtracts out the integer date and rank so that consecutive months (ie consecutive integers) are grouped as the same integer
from
#c c
inner join test.vw_info_h h
on h.effective_date <= c.bom
and (h.expiration_date is null or h.expiration_date > c.bom)
I understand in theory what is happening with the grouping functionality.
How does multiplying year * 12 + month work? Why do we multiply the year? What is happening in the backend?
The year component of a date is an integer value. Since there are 12 months in a year, multiplying the year value by 12 provides the total number of months that have passed to get to the first of that year.
Here's an example. Take the date February 11, 2012 (20120211 in CCYYMMDD format)
2012 * 12 = 24144 months from the start of time itself.
24144 + 2 months (february) = 24146.
Multiplying the year value by the number of months in a year allows you to establish month-related offsets without having to do any coding to handle the edge cases between the end of one year and the start of another. For example:
11/2011 -> 24143
12/2011 -> 24144
01/2012 -> 24145
02/2012 -> 24146

Resources