I have a table of reports with their dates, which are periodic, meaning that there should be a report every 6 months. I have a calendar in my application that I use to display dates for reports that have not yet been done, as a means of planning into the future. Currently, I have managed to display reports that have not yet been done for a six month interval. For example, if I had a report on 01.01.2017, the calendar will correctly show that a new report is needed on 01.07.2017 when passed a 30 day interval that contains this date. This is the query I use for this:
SELECT a.komitent_id,
DATE_FORMAT(MAX(str_to_date(a.report_date, '%d.%m.%Y')), "%d.%m.%Y"),
DATE_FORMAT(MAX(DATE_ADD(str_to_date(a.report_date, '%d.%m.%Y'), INTERVAL 6 MONTH)), "%d.%m.%Y") 'datum_isteka'
FROM izvjestaji_aparata a
WHERE str_to_date(a.report_date, '%d.%m.%Y') < str_to_date(:start, '%d.%m.%Y')
AND DATE_ADD(str_to_date(a.report_date, '%d.%m.%Y'), INTERVAL 6 MONTH)
BETWEEN str_to_date(:start, '%d.%m.%Y') AND str_to_date(:end, '%d.%m.%Y')
GROUP BY a.komitent_id;
Start and end params are passed from PHP and reprsent a monthly interval(from FullCalendar)
But now I want to show reports for a range of 01.01.2018 to 31.01.2018. For example, if I had a report with date between that 1, 1.5, 2, 2.5, etc. years ago or(6, 12, etc.) months ago I should show it in this calendar.
I have been thinking of trying to do something with TIMESTAMPDIFF and MOD, 6 = 0, but I cannot use it for dynamic parameters(range).
Is there any way I can achieve this with a query, or I have to employ PHP logic to do this?
EDIT:
To rephrase, the problem is comparing dates to all dates in passed range :start to :end. I need every date from this range whose difference with a.report_date in months is divisible by 6.
Related
I probably made a poor decision years ago where I have been storing intervals in a char(3) column with values such as "1M" or "3M" or "1Y". This helps me store info about recurring invoices. "1M" means the invoice renews every 1 month.
Here is a sample for the database : https://i.imgur.com/D8oKaV3.png
The reason of this poor design is because I calculate the next invoice date through a php function :
function increment_date($date, $increment)
{
$new_date = new DateTime($date);
$new_date->add(new DateInterval('P' . $increment));
return $new_date->format('Y-m-d');
}
so that I can pass it arguments such as "P1M" which was actually very convenient for DateInterval
I now wish I stored them such as "1 month" instead of "1M", because I am stuck when try to run the following dynamic SQL request :
SELECT SUM(invoice_total) prevision_for_current_month
FROM lf_invoice_amounts a
JOIN lf_invoices_recurring r
ON r.invoice_id a.invoice_id
WHERE (month(recur_next_date) = 5 and year(recur_next_date)= 2020)
OR (month(recur_next_date - INTERVAL recur_frequency) = 5 and year(recur_next_date - INTERVAL recur_frequency) = 2020)
The part with month(recur_next_date - INTERVAL recur_frequency) fails and throws an error because it runs such as month(recur_next_date - INTERVAL 1M) which mySQL does not understand, while the following would have been correct : month(recur_next_date - INTERVAL 1 month)
The purpose of this sql request is to estimate all the money that should come in during current month, from invoices that are recurring every month/3 months/year/2 weeks/etc
I cannot refactor the whole code base at this moment. What would be a possible workaround?
TL;DR : How do I transform a column that contains value "1M" into "1 month" so that I can run SQL requests with intervals. (same for "3M" or "1Y" or "1M" or "2W" or "30D" or "31D", etc).
Ugly solutions also welcome. I'm currently think about a big nest of replace() maybe like month(recur_next_date - INTERVAL replace(recur_frequency, 'M', ' month')) ?
Unfortunately converting your recur_frequency into a string like 1 month isn't going to help, as the syntax for intervals requires that unit be one of YEAR, MONTH, DAY etc. i.e. one of a fixed set of specifiers, not a string, so trying to use something like INTERVAL recur_frequency or even INTERVAL recur_frequency recur_period won't work. Without using dynamic SQL to insert the value of recur_frequency into the query I think your best option is probably to store the recurrence frequency into one of 4 columns (e.g. recur_year, recur_month, recur_week, recur_day), then you can use a query such as
curdate() - interval recur_year year
- interval recur_month month
- interval recur_week week
- interval recur_day day
Demo on dbfiddle
I currentluy use this custom SQL in Contao SQL to display all entries (metamodel) that are in the future.
SELECT * FROM {{table}} WHERE party_date > UNIX_TIMESTAMP();
Now when I have a entry (party) which is scheduled for 2017/03/28 it won't be displayed when its 2017/03/29.
But how can I keep this entry up until 2017/03/29 - 04:00am in the morning?
Visitors of the website should see this partry up until 4am in the morning (event site).
Is it possible with UNIX_TIMESTAMP() ?
Assuming you want to have 4 hours' gap, you can subtract 4 hours from current datetime and compare the DATE part of party_date and NOW(), e.g.:
SELECT *
FROM table
WHERE DATE(party_date) >= DATE(DATE_SUB(NOW(), INTERVAL 4 HOUR))
By this logic, 2017/03/29 - 04:00am would result in 2017/03/29 and as it's same as date part of party_date, it will be displayed.
Here's MySQL's documentation for datetime functions.
Can somebody help me with my report view problem. I need to view monthly organization report. I do get the report for one month but the problem is it didn't take the first day of the month as the initial. from the day the report has been generate, it is just count 30 days backward. therefore it will take the report for the previous month too since it didn't start from 1 day of every month. this is the code that i used.
SELECT *
FROM aduan_form
WHERE tarikh >= SUBDATE(SYSDATE(), INTERVAL 30 DAYS)
i know there is something wrong with it, i already try to add the format in sysdate() which is :
SELECT *
FROM aduan_form
WHERE tarikh >= SUBDATE(SYSDATE('%Y-%m-01'), INTERVAL 1 MONTH)
but the result still the same. how can i fix this problem?
For Oracle, this would help:
Oracle, Make date time's first day of its month
#MatBailie said: According to http://psoug.org/reference/date_func.html, this should work a dandy...
SELECT TRUNC(yourDateField, 'MONTH') FROM yourTable
I am setting up a scheduled cron job that launches a PHP script every other Monday.
My goal is to run a SQL query that returns work orders based on my company's pay period - each pay period ends on the Friday before I run this scheduled cron job.
I have a SQL table which lists all the pay periods of my company, so this table has two column: payperiod_from (start date range) and payperiod_to (end date range). The work orders that I am pulling have a date assigned to them. I would like to be able to pull the work orders where their date is between payperiod_from and payperiod_to.
Since pay periods end every other friday and this cron job is scheduled on the Monday after that, I can simply use CURDATE() - 3 (3 days before Monday - Friday); to get the payperiod_to date, but how can I run a query that filters work orders who's date fall under the correct pay period?
Hopefully this makes sense to some of you guys.
You may run into trouble trying to set up a cron job for "every other week". There's a StackOverflow answer about that here; note the comment under the answer.
As for selecting work orders whose date falls under the correct pay period, if you're already calculating the pay period end as three days before CURDATE() why not forget about the pay_periods table (or whatever it's named) and calculate the pay period beginning as 16 days before CURDATE():
SELECT * FROM wkorder
WHERE wkorder.orderdate BETWEEN CURDATE() - INTERVAL 16 DAY AND CURDATE - INTERVAL 3 DAY
If you can't get the cron job to reliably run every other week and have to set it to weekly, then you can use the pay_periods table to filter out the off weeks. This will prevent any results if the prior Friday wasn't a pay-period end date because there won't be a match in the pay_period table:
SELECT * FROM wkorder
INNER JOIN pay_period ON
payperiod_to = CURDATE() - INTERVAL 3 DAY AND
wkorder.orderdate BETWEEN pay_period.payperiod_from AND pay_period.payperiod_to
Finally, please note that the queries above are syntactically correct but they're not fully tested. It's too much of a leap to go from the information in the question to actual table structures.
I need to calculate a difference between a starting date/time and an ending date/time. But, I only want to do this for the 5-day work week (exclude Sat/Sun as days). What is the best way to do this? My thought is that from the date, I'll have to get the day of the week and if it is a weekday, then I add to the accumulator. If it's not, then I don't add anything.
I'm sure someone has done this before, but I couldn't seem to find anything searching. Any links or other help would be very useful.
Many thanks,
Bruce
DAYOFWEEK returns 1 for Sunday and 7 for Saturday. I'm not sure how your schema is set up, but this will perform a TIMEDIFF of two dates that are on a Monday - Friday work week.
select TIMEDIFF(date1,date2) from table
where DAYOFWEEK(date1) not in (1,7) and DAYOFWEEK(date2) not in (1,7)
MySQL DATE/TIME functions
EDIT: From Bruce's comment about holidays. If you have a table full of holiday dates, something like this would work to exclude processing those days:
select TIMEDIFF(date1,date2) from table
where date1 not in (select holiday from holiday_table) and
date2 not in (select holiday from holiday_table) and
DAYOFWEEK(date1) not in (1,7) and DAYOFWEEK(date2) not in (1,7)
NETWORKDAYS() "Returns the number of whole working days between start_date and end_date. Working days exclude weekends and any dates identified in holidays. Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days worked during a specific term." according to the Excel 2007 help file.
The "between" description is a bit inaccurate because it includes the start and end dates, i.e. networkdays(21-01-2010. 22-01-2010) = 2. It also takes no account of times.
Here's a function in PHP that will give the same results. It doesn't work properly if the end date is less than the start date, nor does do anything about holidays (see below the function).
function networkdays($startdate, $enddate)
{
$start_array = getdate(strtotime($startdate));
$end_array = getdate(strtotime($enddate));
// Make appropriate Sundays
$start_sunday = mktime(0, 0, 0, $start_array[mon], $start_array[mday]+(7-$start_array[wday]),$start_array[year]);
$end_sunday = mktime(0, 0, 0, $end_array[mon], $end_array[mday]- $end_array[wday],$end_array[year]);
// Calculate days in the whole weeks
$week_diff = $end_sunday - $start_sunday;
$number_of_weeks = round($week_diff /604800); // 60 seconds * 60 minutes * 24 hours * 7 days = 1 week in seconds
$days_in_whole_weeks = $number_of_weeks * 5;
//Calculate extra days at start and end
//[wday] is 0 (Sunday) to 7 (Saturday)
$days_at_start = 6 - $start_array[wday];
$days_at_end = $end_array[wday];
$total_days = $days_in_whole_weeks + $days_at_start + $days_at_end;
return $total_days;
}
To take holidays into account, you'd have to work out the number of days using this function, then use a query like
Select count (holiday_date) from holidays
where holiday_date between start_date and end_date
and DAYOFWEEK(holiday_date) not in (1,7)
Be careful that there isn't a problem with the end_date being treated as 00:00 (i.e. first thing in the morning) - you may have to condition it to be 23:59:59 so that it works properly. It all depends on how your holidays are stored.
To return the holidays in the same time period and subtract that from the number you first thought of.