Here is my current formula:
if left ({Command.NextDueDate},7) = "2016-01" then "January'16"
else if left ({Command.NextDueDate},7) = "2016-02" then "February'16"
...
else if left ({Command.NextDueDate},7) = "2017-01" then "January'17"
I take date Strings from my database formatted like 2016-01-05, 2016-01-06...2017-01-01. I use these as columns.
I want to look for the year (16), then month (01), then for next column look for year (16) again and month (2). Then I'd like to interpret the month as shortened month names, like Jan or Feb. Finally I'd want to join the data back together. So in the end it will work like this:
16 + 01 becomes Jan16
16 + 02 becomes Feb16
17 + 01 becomes Jan17
How can I do this without manually entering an if-else clause for each month of each year?
No need to hard code it - Use this formula to get the month and year:
MonthName(Month(CDateTime({Command.NextDueDate}))) +
Right(Left({Command.NextDueDate},4),2);
Don't use if/else statements when you can get the information through clever use of formulas. There are a few ways you can make Ankur's formula even better:
To get the short Month name, use: MonthName(Month(CDateTime({Command.NextDueDate})),True) The True at the end shortens the name to "Jan", "Feb", "Mar" and so on.
To get the Year, use Year(CDateTime({Command.NextDueDate}))
Combine the two formulas and it does all the work for you:
MonthName(Month(CDateTime({Command.NextDueDate})),True) +
Year(CDateTime({Command.NextDueDate}))
Related
I'm currently trying to understand some code and for the life of me I can't figure out what the negative/minus operator is doing inside of the CASE statement. When I try removing it, it breaks the visualization.
This is part of a bigger puzzle of figuring out exactly what this portion of the query does. I know it's a counter of some sort that checks the year, and if the year is 2017, then extracts the month. If it isn't 2017, then it extracts the month 12 months from now, but I'm not sure how it works beyond that. Any insight into the negative/minus operators in this CASE statement or this function in general would be appreciated.
MAX(
CASE WHEN (- CASE WHEN EXTRACT(year FROM premium_creation_date) = 2017 --?? some sort of counter
THEN EXTRACT(month FROM premium_creation_date)
ELSE
EXTRACT(month FROM premium_creation_date) + 12
END
+ CASE WHEN EXTRACT(year FROM user_creation_date) = 2017
THEN EXTRACT(month FROM user_creation_date)
ELSE
EXTRACT(month FROM user_creation_date) + 12
END
) = 1
THEN unique_user_count
ELSE
NULL
END
) AS restriction
The - changes the sign of the result of the CASE.
The CASE gives the month of the premium_creation_date when the year of that date is 2017. Otherwise it's the month plus 12.
So e.g. 2017-05-04 yields 5 in the CASE, which makes -5 after the - took effect. For 2018-07-06 that would be 7 + 12 = 19 and therefore -19.
Actually you could turn this whole thing around and subtract the first CASE from the second. That may look clearer.
recently I asked how to convert calendar weeks into a list of dates and received a great and most helpful answer:
convert calendar weeks into daily dates
I tried to apply the above method to create a list of dates based on a column with "year - month". Alas i cannot make out how to account for the different number of days in different months.
And I wonder whether the package lubridate 'automatically' takes leap years into account?
Sample data:
df <- data.frame(YearMonth = c("2016 - M02", "2016 - M06"), values = c(28,60))
M02 = February, M06 = June (M11 would mean November, etc.)
Desired result:
DateList Values
2016-02-01 1
2016-02-02 1
ect
2016-02-28 1
2016-06-01 2
etc
2016-06-30 2
Values would something like
df$values / days_in_month()
Thanks a million in advance - it is honestly very much appreciated!
I'll leave the parsing of the line to you.
To find the last day of a month, assuming you have GNU date, you can do this:
year=2016
month=02
last_day=$(date -d "$year-$month-01 + 1 month - 1 day" +%d)
echo $last_day # => 29 -- oho, a leap year!
Then you can use a for loop to print out each day.
thanks to answer 6 at Add a month to a Date and answer for (how to extract number with leading 0) i got an idea to solve my own question using lubridate. It might not be the most elegant way, but it works.
sample data
data <- data_frame(mon=c("M11","M02"), year=c("2013","2014"), costs=c(200,300))
step 1: create column with number of month
temp2 <- gregexpr("[0-9]+", data$mon)
data$monN <- as.numeric(unlist(regmatches(data$mon, temp2)))
step 2: from year and number of month create a column with the start date
data$StartDate <- as.Date(paste(as.numeric(data$year), formatC(data$monN, width=2, flag="0") ,"01", sep = "-"))
step 3: create a column EndDate as last day of the month based on startdate
data$EndDate <- data$StartDate
day(data$EndDate) <- days_in_month(data$EndDate)
step 4: apply answer from Apply seq.Date using two dataframe columns to create daily list for respective month
data$id <- c(1:nrow(data))
dataL <- setDT(data)[,list(datelist=seq(StartDate, EndDate, by='1 day'), costs= costs/days_in_month(EndDate)) , by = id]
I've seen threads where the document has Start Date and End Date "widgets" where users type in their dates, however, I'm looking for a dynamic solution, for example on the table below, when I select a date, say "1/1/2004", I only want to see active players (this would exclude Michael Jordan only).
Jersey# Name RookieYr RetirementYr Average PPG
23 Michael Jordan 1/1/1984 1/1/2003 24
33 Scotty Pippen 1/1/1987 1/1/2008 15
1 Derrick Rose 1/1/2008 1/1/9999 16
25 Vince Carter 1/1/1998 1/1/9999 18
The most flexible way is to IntervalMatch the RookieYr * RetireYr dates into a table of all dates. See http://qlikviewcookbook.com/recipes/download-info/count-days-in-a-transaction-using-intervalmatch/ for a complete example.
Here's the interval match for your data. You'll can obviously create your calendar however you want.
STATS:
load * inline [
Jersey#, Name, RookieYr, RetirementYr, Average, PPG
23, Michael Jordan, 1/1/1984, 1/1/2003, 24
33, Scotty Pippen, 1/1/1987, 1/1/2008, 15
1, Derrick Rose, 1/1/2008, 1/1/9999, 16
25, Vince Carter, 1/1/1998, 1/1/9999, 18
];
let zDateMin=37000;
let zDateMax=40000;
DATES:
LOAD
Date($(zDateMin) + IterNo() - 1) as [DATE],
year( Date($(zDateMin) + IterNo() - 1)) as YEAR,
month( Date($(zDateMin) + IterNo() - 1)) as MONTH
AUTOGENERATE 1
WHILE $(zDateMin)+IterNo()-1<= $(zDateMax);
INTERVAL:
IntervalMatch (DATE) load RookieYr, RetirementYr resident STATS;
left join (DATES) load * resident INTERVAL; drop table INTERVAL;
There's not much to it you need to load 2 tables one with the start and end dates and one with the calendar dates then you interval match the date field to the start and end field and from there it will work the last join is just to tidy up a bit.
The result of all of that is this ctrl-t. Don't worry about the Syn key it is required to maintain the interval matching.
Then you can have something like this.
Derrick Rose is also excluded since he had not started by 1/1/2004
I have a combo chart which shows the average days it took for a person to pay their bill.
The Dimension of the Chart is = [Pay Month Year last 12 months]
There are no Dimension limits
There is 1 expression which is called Average and its definition is:
avg({< InvoicefromSqlType = {'Invoices'},[Is Invoice Paid] = {'Y'},[Is Positive Amount] = {'Y'},[Is Paid last 12 months] = {'Y'},DueGroups=,[Pay Month Year last 12 months]=>}[Days to Pay])`
Its is sorted by expression, which is [Pay Month Year last 12 months]
Now the above fields are built up like this:
[Pay Month Year last 12 months]
If([Pay Date] >= '$(vPeriodS12)',[Pay Month year]) as [Pay Month Year last 12 months],
PayLoadOrder:
Load * Inline [Pay Month Year last 12 months
May-2014
Jun-2014
Jul-2014
Aug-2014
Sep-2014
Oct-2014
Nov-2014
Dec-2014
Jan-2015
Feb-2015
Mar-2015
Apr-2015
May-2015
];
Now what is happening is every month when it reaches the end, the next month is needing to be manually added and the first month removed (e.g. in the above I would delete the line May-2014 and at the end add the line Jun-2015)
Also if there are months defined which there is no data for yet i.e. you have Jun-2015 hard coded and the current month this May-2015 then Jun-2015 will show data from 2014 and the order of the months will get mixed up.
What I want to do is completely remove the need to hard code the months above and have this done its self.
If there are any more information you need let me know
Instead of using a "sort order" table that you may have to manually update, it may be worth doing the following:
Create a new field derived from [Pay Date] that returns a month and year that you can sort. For example:
dual(date(makedate(year([Pay Date]),num(month([Pay Date]))),'MMM-yyyy'),
year([Pay Date]) * 100 + num(month([Pay Date]))) as PayMonthYear
Here, the dual function allows you to associate a different representation of a field value to its underlying value. For example, here we set the underlying data to be the year of [Pay Date] added to the month, but state that it should be displayed as MMM-yyyy. For example, internally, QV still sees the value 201502, but displays it as Feb-2015. This means you can sort it correctly based on its underlying value.
Using dual is a big topic, please consult the built-in help for QV for more information.
Change your chart dimension from [Pay Month Year last 12 months] to use PayMonthYear and set the sort to ascending. This would then mean that your months would be sorted correctly, even if a new month was added.
Remove table PayLoadOrder from your script.
Alternative Method
An alternative would be to use a calendar table which joins on your Pay Date field. This would achieve the same thing, however, you could also integrate your "year to date" indicator into the calendar as well and remove it from your main table. An example I quickly threw together is shown below:
MinMax:
LOAD
Max([Pay Date]) AS MaxDate,
Min([Pay Date]) AS MinDate
RESIDENT MyData;
LET varMinDate = Num(Peek('MinDate',0,'MinMax')); // 0 is first record
LET varMaxDate = Num(Peek('MaxDate',-1,'MinMax')); // -1 is last record
LET varToday = Num(Today());
MasterCalendar:
LOAD
monthstart([Pay Date]) >= monthstart(AddMonths(Today(),-12)) as PaidInLast12MonthsFlag,
dual(date(makedate(year([Pay Date]),num(month([Pay Date]))),'MMM-yyyy'),year([Pay Date]) * 100 + num(month([Pay Date]))) as PayMonthYear
[Pay Date];
LOAD
date($(varMinDate) + RecNo() - 1,'DD/MM/YYYY') as [Pay Date]
AUTOGENERATE num($(varMaxDate)) - num($(varMinDate)) + 1;
DROP TABLE MinMax;
So, in the above, the field PaidInLast12MonthsFlag is equal to -1 if the value of the [Pay Date] field occurs in the last 12 months, 0 otherwise. You could use this in your set analysis expression as a filter. Furthermore, you can use PayMonthYear as your chart dimension.
My client has a report that accepts a date range to get a report showing projected revenue. So, a user would enter a date range of '1/1/2015 to 1/31/2015' and the report should return data only in the range '1/1/2015 to 1/31/2015 grouped by week. I am instead for the week of 12/29/2014 (which 1/1/2015 fall into) and 2/1/2015 (which 1/31/2015 falls into). The report is intended to group by week, but I do not want days on the report that are earlier than the start date parameter or later than the end date parameter.
The sql statement for this report is:
SELECT job.job, job.status, job.customer_po, job.part_number, job.unit_price,
job.price_uofm, delivery.promiseddate, delivery.remaining_quantity, job.build_to_stock, job.description, job.make_quantity, job.pick_quantity, job.shipped_quantity, job.lead_days
FROM dbo.delivery as delivery RIGHT OUTER JOIN db.job as job on delivery.job = job.job
WHERE job.build_to_stock = 0 AND (job.status = 'active' OR job.status = 'hold' OR job.status = 'pending')
The date range is from this code and parameters:
Max – Maximum(?Date Range)
Min – Minimun(?Date Range)
Date Range - "From " & {#Min} & " to " & {#Max}
This is the group expression
Group 2 Name - GroupName ({#Adj Date 2}, "weekly") & " thru " & cdate(GroupName ({#Adj Date 2}, "weekly"))+6
This is the select expression
{#Date} = {?Date Range} and
not {Job.Build_To_Stock} and
{Job.Status} in ["Active", "Hold", "Pending"]
Do you know how I can prevent the "overflow" of dates outside of date range?
Thx
As long as you have date filtering in your record selection formula there will not be any "overflow" outside of that range. If you've got {Record.Date} in Minimum({?DateRange}) to Maximum({?DateRange}), which it sounds like you do, then your report will not contain any records outside of the parameter regardless of how you group them.
Your problem might stem from over-complicating or misinterpreting the grouping. All you need to do is group by {Record.Date} and select "Group by week" in the grouping options... you don't need any complicated formulas to break it out by week. But be aware that the way weeks are referred to is by their starting date. For example, if you had a record with a date of Feb. 19, 2015, that record would fall into the group labeled "Feb. 15, 2015" even if your {?DateRange} parameter was Feb. 18 - Feb. 15.