How to add new year? - coldfusion

I would like to increment school year every time after previous school year is over. For example my current code looks like this:
WHERE schooldt BETWEEN '07/01/2016' AND '06/30/2017'
So once school year is over 06/30/2017 I would like to set new start date and new end date automatically. I was thinking about using dateAdd() in cold fusion. Is there any other way to do this and what would be the most efficient?
Thanks in advance.

I would like to increment school year every time after previous school year is over
Then implement logic that changes the date values used in your query based on the current month. If the current month is earlier than July, then you know the current school year is still in progress. So deduct one year to calculate the start date. Otherwise, move to the next year.
<cfset today = now()>
<!--- If the current school year is still in progress --->
<cfif month(today) lt 7>
<cfset startDate = createDate( year(today) - 1, 7, 1)>
<cfset endDate = createDate( year(today), 6, 30)>
<!--- Otherwise, move to next year --->
<cfelse>
<cfset startDate = createDate( year(today), 7, 1)>
<cfset endDate = createDate( year(today) + 1, 6, 30)>
</cfif>
As far as querying, two things to keep in mind:
Date strings are ambiguous. Always use date objects instead.
Be careful with date comparisons and the BETWEEN operator. If the SchoolDt column contained both a date and time, the result may not be what you expected. A more flexible construct (that stills works even if a column contains both date and time) is:
WHERE SchoolDt >= <cfqueryparam value="#startDate#" cfsqltype="cf_sql_date">
AND SchoolDt < <cfqueryparam value="#dateAdd('d', 1, endDate)#" cfsqltype="cf_sql_date">
If you are using new Query(), parameterize the sql string and use addParam instead:
yourQuery.addParam( name="startDate"
, value="#startDate#"
, cfsqltype="cf_sql_date" );
yourQuery.addParam( name="endDate"
, value="#endDate#"
, cfsqltype="cf_sql_date" );

For the date table answer:
CREATE TABLE calendar (
SeqNum int
, schooldt date NOT NULL PRIMARY KEY
, theYear int
, theMonth tinyint
, theDay tinyint
, schoolyear int
, isSchoolDay tinyint /* Can count schooldays */
, isHoliday tinyint /* Can count holidays. */
)
DECLARE #StartDate date = '1/1/2010'
/* Build tally table with 10^x rows */
; WITH TallyTable (x) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(x) -- 10 days
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(x) -- 100 days
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(x) -- 1000 days
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(x) -- 10000 days
)
INSERT INTO calendar ( SeqNum, schooldt, theYear, theMonth, theDay, schoolyear, isSchoolDay, isHoliday )
SELECT
tt.x AS SeqNum
, d1.theDate
, d2.theYear
, d2.theMonth
, d2.theDay
, d2.theSchoolYear
, 1 , 0 /* Defaults for isSchoolDay and isHoliday. Add if needed. */
FROM TallyTable tt
CROSS APPLY (
SELECT theDate = DATEADD(dd, (tt.x-1), #StartDate) /* Starting date */
) d1
CROSS APPLY
(
SELECT theYear = DATEPART(yy,d1.theDate)
, theMonth = DATEPART(mm,d1.theDate)
, theDay = DATEPART(dd,d1.theDate)
, theSchoolYear = CASE WHEN DATEPART(mm,d1.theDate) < 7 THEN DATEPART(yyyy,d1.theDate) ELSE DATEPART(yyyy,d1.theDate)+1 END
) d2;
Something along those lines will give you a list of days for several years out. Then you can join on this table to get the range that you want.
To improve the table, you can include information to label Holidays and Weekends, so that you only see days that school is actually in session. And most importantly, you can filter it all by the current school year that you wish to see. You don't have to worry about incrementing the school year when it's time to change.
But it again begs the question, what is your ultimate goal of this query? There may be even better ways to accomplish your ultimate goal.

Related

Pivot with dynamic DATE columns

I have a query that I created from a table.
example:
select
pkey,
trunc (createdformat) business_date,
regexp_substr (statistics, 'business_ \ w *') business_statistics
from business_data
where statistics like '% business_%'
group by regexp_substr(statistics, 'business_\w*'), trunc(createdformat)
This works great thanks to your help.
Now I want to show that in a crosstab / pivot.
That means in the first column are the "business_statistics", the column headings are the "dynamic days from business_date".
I've tried the following, but it doesn't quite work yet
SELECT *
FROM (
select
pkey,
trunc(createdformat) business_date,
regexp_substr(statistics, 'business_\w*') business_statistics
from business_data
where statistics like '%business_%'
)
PIVOT(
count(pkey)
FOR business_date
IN ('17.06.2020','18.06.2020')
)
ORDER BY business_statistics
If I specify the date, like here 17.06.2020 and 18.06.2020 it works. 3 columns (Business_Statistic, 17.06.2020, 18.06.2020). But from column 2 it should be dynamic. That means he should show me the days (date) that are also included in the query / table. So that is the result of X columns (Business_Statistics, Date1, Date2, Date3, Date4, ....). Dynamic based on the table data.
For example, this does not work:
...
IN (SELECT DISTINCT trunc(createdformat) FROM BUSINESS_DATA WHERE statistics like '%business_%' order by trunc(createdformat))
...
The pivot clause doesn't work with dynamic values.
But there are some workarounds discuss here: How to Convert Rows to Columns and Back Again with SQL (Aka PIVOT and UNPIVOT)
You may find one workaround that suits your requirements.
Unfortunately, I am not very familiar with PL / SQL. But could I still process the start date and the end date of the user for the query?
For example, the user enters the APEX environment as StartDate: June 17, 2020 and as EndDate: June 20, 2020.
Then the daily difference is calculated in the PL / SQL query, then a variable is filled with the value of the entered period using Loop.
Example: (Just an idea, I'm not that fit in PL / SQL yet)
DECLARE
startdate := :P9999_StartDate 'Example 17.06.2020
enddate := P9999_EndDate 'Example 20.06.2020
BEGIN
LOOP 'From the startdate to the enddate day
businessdate := businessdate .... 'Example: 17.06.2020,18.06.2020,19.06.2020, ...
END LOOP
SELECT *
FROM (
select
pkey,
trunc(createdformat) business_date,
regexp_substr(statistics, 'business_\w*') business_statistics
from business_data
where statistics like '%business_%'
)
PIVOT(
count(pkey)
FOR business_date
IN (businessdate)
)
ORDER BY business_statistics
END;
That would be my idea, but I fail to implement it. Is that possible? I hope you understand what I mean

PowerBI DAX: Date periods identification formula issue on previous 3 months

I have a sales data set and want to create a calculated column to be used as legend on graphs and also use as filter of dates but I'm struggling on with the part that identificate the previous 3 months before the last month.
My formula looks like this:
Period identification =
IF('Base'[Date]=CALCULATE(MAX('Base'[Date]);FILTER('Base';'Base'[FYTD]="FY1920"));"LM";
IF(
AND('Base'[Date]>=
DATEADD(CALCULATE(MAX('Base'[Date]);FILTER('Base';'Base'[FYTD]="FY1920"));-3;MONTH);
'Base'[Date]<CALCULATE(MAX('Base'[Date]);FILTER('Base';'Base'[FYTD]="FY1920")));"P3M";
IF('Base'[Date]=CALCULATE(MAX('Base'[Date]);FILTER('Base';'Base'[FYTD]="FY1819"));"LM YA";"OTHERS")))
The first IF() (Last Month - LM) and the last (Last Month Year Ago - LM YA) are working fine. I just can't find a way of PBI accept the kind of calculus I'm trying to do in the middle to identificate the previous 3 months before the last month.
*The [FYTD] is another calculated column that identificate the corresponding fiscal year of the [Date].[Month]
Thanks in advance
I think you can make this much easier using the handy EOMONTH function to move through time.
Try something like this where RefDate is the date that you want to use to calculate dates relative to. I'm using the end last month.
Period =
VAR RefDate = EOMONTH ( TODAY (), -1 )
RETURN
SWITCH (
TRUE (),
EOMONTH ( Base[Date], 0 ) = EOMONTH ( RefDate, 0 ), "LM",
Base[Date] > EOMONTH ( RefDate, -4 ), "P3M",
EOMONTH ( Base[Date], 0 ) = EOMONTH ( RefDate, -12 ), "LM YA",
"OTHERS"
)
Note:SWITCH(TRUE(),...) returns the result for the first condition that evaluates to TRUE().

Power Bi DAX: Relative Date Filtering

I have a quick question, is there a way to filter data from set days between 2 months?
E.g filter from 16-15 of Jan-Feb.
Is this possible?
For example i have used a measure to calculate days between dates
Last Assessment 16-15
= CALCULATE(SUM('Table1'[Duration1]),'Table1'[Start],
DATESBETWEEN('Calendar'[Date], [Assessment Date], [Assessment one month]))
Assessment Date = if(DAY(TODAY())<16,DATE(YEAR(TODAY()),MONTH(TODAY())-1,15),
DATE(YEAR(TODAY()),MONTH(TODAY()),15))
Assessment one month = EDATE([Assessment Date],-1)+1
Assessment 6 = EDATE([Assessment Date],-6)+1
Assessment 12 = EDATE([Assessment Date],-12)+1
The last assessment does show from the 16th of 2 months ago to last months 15th e.g Dec 16th - Jan 15th.
But i need to show from last 6 months and the last 1 year.
How can i work this out so i can show the Last 6 months and 1 year.
So far i have had to use a date filter to manually select the dates which i want to stop and have it be automatic.
If it is just the last 6 months or the last year you could make a custom column in the query editor (this would be the easiest way then). Like a filter flag:
'Includes the current month
Last 6 Months Flag = Date.IsInPreviousNMonths([YourDate], 6) or Date.IsInCurrentMonth([YourDate])
'Without the current month
Last 6 Months Flag = Date.IsInPreviousNMonths([YourDate], 6)
The same for last year:
Last Year Flag = Date.IsInPreviousYear([YourDate])
Drag and drop these custom columns as filter on your report and you are done.
consider following measure for Latest assessment (basically what you had before with EDATE for safe Dec/Jan handling and with variables for better performance)
Latest Assessment Date =
VAR __today = TODAY()
RETURN
IF (
DAY ( __today ) < 16,
DATE ( YEAR ( __today ), MONTH(EDATE(__today, - 1)), 15 ),
DATE ( YEAR ( __today ), MONTH ( __today ), 15 )
)
then you have this measure for handling 1/6/12 scenarios, you just replace 12 with whatever number of months you need
Measure 12 =
VAR __nrOfMonthsBack = 12
VAR __lastAssesment = [Latest Assessment Date]
RETURN
CALCULATE (
SUM ( Table1[Duration] ),
ALL('Calendar'), --this will reset any date filters coming from slicers
DATESBETWEEN (
'Calendar'[Date],
EDATE ( __lastAssesment, - __nrOfMonthsBack ) + 1,
__lastAssesment
)
)
EDIT: added ALL('Calendar') to disable filters coming from slicers

Custom Calendar for sales data aggregated by weeks and months

I am struggling with creating Custom Calendar that would allow me to use time intelligence function on data that is already aggregated by weeks and months. The original table with transactions contains over 20M rows, so for performance and space saving reasons the grouping is necessary and I perform it while querying the database in SQL Server. I want to create:
Month vs Same Month Last Year
Week vs Same Week Last Year
Year-To-Date vs Last Year Year-To-Date (by Months)
Year-To-Date vs Last Year Year-To-Date (by Weeks)
My idea was to assign some dummy dates to each row of data, and then create custom calendar that would have each of these dummy dates along with other details. I just cannot figure out the key (how to create dummy date having Year number Month number and Week number - please keep in mind that for example Week 5 of 2020 is partially in January and partially in February)
Is it possible? Maybe I have to create 2 separate Calendars, one for Weeks and one for Months?
Add calculated table:
Calendar =
GENERATE (
CALENDAR (
DATE ( 2016, 1, 1 ),
DATE ( 2020, 12, 31 )
),
VAR VarDates = [Date]
VAR VarDay = DAY ( VarDates )
VAR VarMonth = MONTH ( VarDates )
VAR VarYear = YEAR ( VarDates )
VAR YM_text = FORMAT ( [Date], "yyyy-MM" )
VAR Y_week = YEAR ( VarDates ) & "." & WEEKNUM(VarDates)
RETURN
ROW (
"day" , VarDay,
"month" , VarMonth,
"year" , VarYear,
"YM_text" , YM_text,
"Y_week" , Y_week
)
)
You can customize it. In relation pane connect your Date field of FactTable (which is by week, as you say. This table can have missing dates and duplicate dates, to be precise) to the field Date of the Calendar table (which has unique Date, by day). Then, in all your visuals or measures always use the the Date field of the Calendar table. It is a good practice to hide the field Date in your FactTable.
More explanations here https://stackoverflow.com/a/54980662/1903793

Sum Current Month + 2 AND Next Month + 1 and Month After that

I have a metric I need to replicate in DAX for PowerBI and I am not quite sure how to do it.
The basics are this:
Fact Table: Opportunity
Dimensions: Created Date, Closed Date
The metric goes like this, I will just give an example, because I really dont know how to explain it.
SUM OF:
Created in FEB and Closed Date in FEB, MAR, APR
Created in MAR and Closed Date in MAR, APR
Created in APR and Closed in APR
This would happen for each month in the table/matrix.
Seems like I would need some variables something like
Measure =
VAR Month1 = SUM(ClosedOpps) where ClosedDate between CurrentMonth and CurrentMonth + 2
VAR Month2 = SUM(CLosedOpps) where ClosedDate betwwen CurrentMonth + 1 and CurrentMonth + 2
VAR Month3 = SUM(ClosedOpps) where ClosedDate = CurrentMonth + 2
Return Month1 + Month2 + Month3
My understanding is, the Closed Date filter would be the Table/Matrix Visual when I drag the column MonthYear into the visual
EDIT:
Here is a simplified replica of what they are doing in Excel
So The data on the left is the fact table. You can see when the Opps are created, when they are closed. I added in the Created MonthYear and the Closed MonthYear. The Pivot is what they have now in Excel. Dates across the top (Columns) are Created YearMonth, Dates for the Rows are Closed YearMonth.
I need to be able to SUM the numbers inside the of I3:K5 which total 5500 in the example.
UPDATE:
So I have added in a suggested Date Dimension table, Duplicated it (One for Open Date, one for Closed Date) I added a column DateDIM_KEY to each which is just a numerical index. The fact table has these keys, and they are loaded off of the same date range (2013 to 2030). The column ActualValue in the fact table is the column we would SUM.
Here is the updated Fact table sample. I pulled the DateDIM_KEY values directly from the date dimension for those dates.
You need a good date dimension. And you need to have it roleplaying for OpenDate and CloseDate. There are many good date dimensions out there. I like mine.
Assuming that you're putting 'OpenDate'[Month] on an axis label.
Opportunity Value = SUM ( 'Opportunity'[Value] )
MyMeasure iterator =
// start of the month on the current row of a pivot/axis label of a chart
VAR CurrentMonthStart = MIN ( 'OpenDate'[Date] )
// End of the month 2 months out
VAR ThreeMonthsOutEnd = EOMONTH ( CurrentMonthStart, 2 )
// This represents one row per month. You could also use a MonthAndYear type field.
// We will walk through the three open months we care about, and in each will sum
// the value for the opportunities opened in that month, with additional filters.
VAR NextThreeOpenMonths =
CALCULATETABLE (
VALUES ( 'OpenDate'[MonthIndex] ),
ALL ( 'OpenDate' ),
DATESBETWEEN ( 'OpenDate'[Date], CurrentMonthStart, ThreeMonthsOutEnd )
)
RETURN
// Iterate each OpenMonth
SUMX (
NextThreeOpenMonths,
// On each step of the iteration, grab the start of the currently iterated month
VAR IterMonthStart = CALCULATE ( MIN ( 'OpenDate'[Date] ) )
RETURN
CALCULATE (
[Opportunity Value],
// There is date context from visuals we want to ignore:
ALLEXCEPT ( 'OpenDate', 'OpenDate'[MonthIndex] ),
// filter CloseDate to be between the start of the currently iterated
// open month and the originally calculated ThreeMonthsOutEnd. The latter
// is static within the scope of the iteration.
DATESBETWEEN ( 'CloseDate'[Date], IterMonthStart, ThreeMonthsOutEnd )
)
)
Also, while writing the previous iterative approach, I realized we could do the work in a single setfilter:
MyMeasure set =
// MonthIndex is in my date dimension - super useful for arithmetic on dates.
// Read the readme.
VAR C = SELECTEDVALUE ( 'OpenDate'[MonthIndex] ) // want a short name below
// Table literal syntax - two column table, where each parenthesized expression
// forms a row. If it were much more, I'd do something clever with generate, but
// six cases are easy to write by hand.
VAR MonthFilters = {
(C, C),
(C, C+1),
(C, C+2),
(C+1, C+1),
(C+1, C+2),
(C+2, C+2)
}
RETURN
CALCULATE (
[Opportunity Value],
TREATAS ( MonthFilters, 'OpenDate'[MonthIndex], 'CloseDate'[MonthIndex] )
)
I like the latter a lot better, but didn't think of it until after writing the iterative version, so I'm leaving both. Set-based should be better performing.
Edit: some screengrabs I forgot:
Here's the relationship diagram for roleplaying date dim:
And here's the visual in action with both measures:
Best thing to do here is to add a custom column (under edit querys) with the date diff per month. Now you can filter after the column LeadTimeInMonth for you scenarios. If you drag and drop your fields into the visual you can filter by this column.
Date.Month([ClosedDAte])-Date.Month([OpenDate])
I am not sure what you really want to evaluate but if you need need exactly ClosedDate between CurrentMonth and CurrentMonth + 2 you can first evaluate the month from the ClosedDate then the month of today and filter after the result.