I have the following tables:
In other words I have a set of customers I follow throughout time. I have a column of the date of their first purchase, and another column with another purchase they made at another time.
I want to make a column which specifies the time period in which the specific purchase was made. The time periods should be defined such that a new period starts the 20th each month and the first period for each customer should therefore be the day they made their first purchase and until the 20th.
This is illustrated below:
What I need:
I have tried to implement this via a handful of if statements like:
WHEN DAY(c.created)<21 and DAY(s.created)<21 and year(c.created)-year(s.created)=0
THEN MONTH(c.created)-MONTH(s.created)+1
WHEN DAY(c.created)>20 and DAY(s.created)<21 and year(c.created)-year(s.created)=0
THEN MONTH(c.created)-MONTH(s.created)+2
and so on.
I want to know if there is an easy(er) and simple(r) way to handle this problem?
I work in MySQL Workbench 6.3 CE
The date of the first day of the calendar month in which a customer made a purchase can be found like this:
DATE_FORMAT(First_purchase , '%Y-%m-01')
So, the date of the first day of your fiscal month, which starts on the 20th of each calendar month, can be found like this.
DATE_FORMAT(First_purchase - INTERVAL 19 DAY, '%Y-%m-01') + INTERVAL 19 DAY
Then, you can use TIMESTAMPDIFF as follows to get the number of months between two of these sorts of numbers.
TIMESTAMPDIFF(
MONTH,
DATE_FORMAT(First_purchase - INTERVAL 19 DAY, '%Y-%m-01') + INTERVAL 19 DAY,
DATE_FORMAT(Date_created - INTERVAL 19 DAY, '%Y-%m-01') + INTERVAL 19 DAY) + 1
That will get you the number of the fiscal month, starting with First_purchase, in which Date_created is found, where the first fiscal month has the number 1.
Using native date arithmetic in place of DAY(), MONTH(), and YEAR() functions is much more likely to keep working over ends of years and in leap years and all that.
Related
I'm trying to get data going back three months for monthly reports. I got to the point where i can get all the data. The problem is that it goes back 3 months based on the current date.
Example: If today is 7th of November it will give me the data up until the 7th of August.
I need it to give me the data going back three months but starting from the first of the month.
Example: today is the 7th of November, I'll need the data starting from the 1st of August.
Here is the code I'm using to get the data from three months back:
SELECT * FROM 'closed_wo_journal' WHERE date_time_stamp > CURDATE() - INTERVAL 3 MONTH
The date_trunc function is just what the doctor ordered:
SELECT *
FROM closed_wo_journal
WHERE date_time_stamp > DATE_TRUNC('MONTH', CURDATE() - INTERVAL '3 MONTH')
Here is logic to get the first of the month, three months ago:
SELECT j.*
FROM closed_wo_journal j
WHERE j.date_time_stamp >= ( CURDATE() - INTERVAL (1 - DAY(CURDATE()) DAY) ) - INTERVAL 3 MONTH )
SELECT DATE(STR_TO_DATE(CONCAT(CONCAT(YEAR('$uDate1'), week), ' Monday'), '%X%V %W') +
INTERVAL (7 - DAYOFWEEK(STR_TO_DATE(CONCAT(CONCAT(YEAR('$uDate1'), week), ' Monday'),
'%X%V %W'))) DAY)
as week_end_date
What this statement does is take the date I give it ($uDate1) and give me the week end date (Saturday) of that week. This works well and I am happy with it, kinda.
I was wondering if there were some things I missed that would either make this more efficient or even if I missed some shortcuts to this.
Any suggestions for me?
week >= WEEK('$uDate1') AND week <= WEEK('$uDate2')
This is in my WHERE clause. So basically if I use this...
DATE('$uDate1', INTERVAL 7 - DAYOFWEEK('$uDate1') DAY)
...then it returns the same day for all records. I need it to be able to go over a span of a few weeks.
I have a column in my database named 'week'. It simply stores an INT that corresponds to the week of the year. (ex. 21 for this week)
I then have two date picker boxes. The output gets the week end date based of each week that is BETWEEN and INCLUDES the days chosen.
5/10/2016 & 5/26/2016 outputs 5/14/2016, 5/21/2016, 5/28/2016
What gets exported to CSV file looks something like this..
WEEK END, LAST NAME, FIRST NAME, ...
5/10/2016, Smith, John, ...
5/26/2016, Jones, James, ...
It outputs anyone who had hours during the week, with the week end date.
SIDE NOTE: I do appreciate the comments and help. I don't want anyone to stress over this though! Just curious if better way. :)
I am not sure why your current SQL is so complicated.
You say it is just to take a date and give me the week end date (Saturday) of that week .
How you are doing this at the moment is:-
Yours is taking the year
Adding the week of the year (I assume - should be WEEK('$uDate1') I think)
Adding on the day as a string (so for example for today it would be 2016 21 Monday )
Changing that string back to a date a datetime value
Converting that datetime value back to a date.
Then taking the year again
Adding the week of the year again
Getting the day of the week of the resulting string. As you have concatenated Monday on to the date then the day of the week will always be 2.
Taking that resulting day of the week and subtracting it from 7. As the day of the week will always be 2 this will always result in 5
Adding on the day as a string (so for example for today it would be 2016 21 Monday ).
This value is then added on to the previously calculated date, taking the Monday date and adding 5 days.
My suggestion was to just use:-
DATE_ADD($uDate1, INTERVAL 7 - DAYOFWEEK($uDate1) DAY)
which is far simpler, and appears to cover your requirements.
EDIT
Looking at your edit you want a list of all the Saturdays for weeks all or partially between 2 passed dates.
If so I think the following will do it and hopefully be more efficient as there is no need to translate dates to and from string. Note it relies on your week table to add to the date, hence only copes with date ranges of up to that many weeks.
SELECT DATE_ADD(DATE_ADD('$uDate1', INTERVAL 7 - DAYOFWEEK('$uDate1') DAY), INTERVAL `week` WEEK) AS aDate
FROM `week`
HAVING aDate BETWEEN '$uDate1' AND DATE_ADD('$uDate2', INTERVAL 7 - DAYOFWEEK('$uDate2') DAY)
ORDER BY aDate
As I mentioned in comment you should move this transformation from mysql query to php code.
I see no reason to do this calculation on mysql side.
http://ideone.com/48zLvF
$week_day = intval(date('w',$uDate1));
if ($week_day<6) {
$end_of_week = $uDate1+(86400*(6-$week_day));
} else {
$end_of_week = $uDate1;
}
I have a table "task_table" containing columns-
Task_id, Start_date, End_date
And I have one more "configuration" table which has the records that tell which days of the week are working days.
This table has two columns -
week_day, isHoliday
and this table contains seven records as week_days are the Monday,Tuesday.....Sunday , and each record has an entry as 1 or 0. If a day is a holiday in any organization then there will be 0 against that day. Like if an organisation has holidays on Wednesday and Friday every week then there will be 0 against Wednesday and Friday only.
Now I want to make a SQL query to get the Task_id, Start_date, End_date, and the count of total days consumed on each task. (These days are the days between task start_date and end_date excluding the holiday days as configured in "configuration" table.)
I don't have time to fully answer this question now, but what I would do is:
Get the date as at the start of the Start_date week, and the date as at the end of the End_date week (you can get this by date_adding an amount of days according to the day of the week.
Then you want to date diff them, divide by seven, multiply by two, and remove any that you would have added (e.g. if the start date was Thursday then you'll need to remove one from the result, as you will have counted one for the Wednesday immediately prior.
I'll write the code out tomorrow (it's late here - something like 14 hours from now or so.) if noone else has suggested a better answer.
Edit: Right, didn't properly read the question, but the tactic still applies with a little fiddling. Speaking of which, here is the fiddle of my solution.
It boils down to the following code:
set #holidaysPerWeek = (select sum(isHoliday) from configuration);
select
Task_id,
((dateDiff(
DATE_ADD(End_Date, INTERVAL 7 - DayOfWeek(End_Date) DAY),
DATE_ADD(Start_Date, INTERVAL -DayOfWeek(Start_Date) + 1 Day)) + 1) / 7)
* #holidaysPerWeek
- (select sum(isHoliday) from configuration where week_day > DayOfWeek(End_Date))
- (select sum(isHoliday) from configuration where week_day < DayOfWeek(Start_Date)),
DayOfWeek(End_Date)
from task_table
This does exactly what I was saying before, but with a variable number of "weekends" spread throughout the week, by first selecting the number of holidays for if the full weeks were covered, then removing holidays that were before or after the start and end dates respectively.
I am trying to write a PHP script that will process recurring payments every month, quarter, year, etc. This script will run as a nightly Cron job.
I don't want to run into a situation where somebody subscribes, say, on the 15th of a January, and then gets billed again on the 1st of February.
Is that what would happen if I checked the last payment against INTERVAL 1 MONTH? Or would it be the same as INTERVAL 30 DAY, and only process the payment again on the 15th of February, which is what I want?
Accroding to MYSQL
If you add MONTH, YEAR_MONTH, or YEAR and the resulting date has a day
that is larger than the maximum day for the new month, the day is
adjusted to the maximum days in the new month:
mysql> SELECT DATE_ADD('2009-01-30', INTERVAL 1 MONTH);
-> '2009-02-28' Date arithmetic operations require complete dates and do not work with incomplete dates such as '2006-07-00' or
badly malformed dates:
Thus if you use the 1 month built in function you do not have to worry when the last day of the month is. MYSQL does all the work for you.
I have a report that is driven by a sql query that looks like this:
SELECT batch_log.userid,
batches.operation_id,
SUM(TIME_TO_SEC(ramses.batch_log.time_elapsed)),
SUM(ramses.tasks.estimated_nonrecurring + ramses.tasks.estimated_recurring),
DATE(start_time)
FROM batch_log
JOIN batches ON batch_log.batch_id=batches.id
JOIN ramses.tasks ON ramses.batch_log.batch_id=ramses.tasks.batch_id
JOIN protocase.tblusers on ramses.batch_log.userid = protocase.tblusers.userid
WHERE DATE(ramses.batch_log.start_time) > "2011-02-01"
AND ramses.batch_log.time_elapsed > "00:03:00"
AND DATE(ramses.batch_log.start_time) < now()
AND protocase.tblusers.active = 1
AND protocase.tblusers.userid NOT in ("ksnow","smanning", "dstapleton")
GROUP BY userid, batches.operation_id, date(start_time)
ORDER BY start_time, userid ASC
Since this is to be compared with the time from the current payperiod it causes an error.
Our pay periods start on a Sunday, the first pay period was 2011-02-01 and our last pay period started the 4th of this month. How do I put that into my where statement to strip the most recent pay period out of the query?
EDIT: So now I'm using date_sub(now(), INTERVAL 2 WEEK) but I really need a particular day of the week(SUNDAY) since it is wednesday it's chopping it off at wednesday.
You want to use DATE_SUB, and as an example.
Specifically:
select DATE_SUB(curdate(), INTERVAL 2 WEEK)
gets you two weeks ago. Insert the DATE_SUB ... part into your sql and you're good to go.
Edit per your comment:
Check out DAYOFWEEK:
and you can do something along the lines of:
DATE_SUB(DATE_SUB(curdate(), INTERVAL 2 WEEK), INTERVAL 2 + DAYOFWEEK(curdate()) DAY)
(I don't have a MySql instance to test it on .. but essentially subtract the number of days after Monday.)
Question isn't quite clear, especially after the edit - it isn't clear now is the "pay period" two weeks long or do you want just last two weeks back from last sunday? I assume that the period is two weeks... then you first need to know how many days the latest period (which you want to ignore, as it isn't over yet) has been going on. To get that number of days you can use expression like
DATEDIFF(today, FirstPeriod) % 14
where FirstPeriod is 2011-02-01. And now you strip that number of days from the current date in the query using date_sub(). The exact expression depends on how the period is defined but you should get the idea...