Mysql date less than week operation - mysql

Suppose you have two datetime values, date_a and date_b
What expression evaluates to true if date_a occurs within or less than the calendar week of date_b?

Just use MySQL's week() function.
Example:
mysql> create table daten (a date, b date);
mysql> insert into daten values ('2011-04-14', '2011-04-12');
mysql> insert into daten values ('2011-04-14', '2011-04-22');
mysql> select * from daten where (week(a)=week(b) and year(a)=year(b)) or a<b;
+------------+------------+
| a | b |
+------------+------------+
| 2011-04-14 | 2011-04-12 |
+------------+------------+
This will give you all records with a in the same or an earlier calendar week than b. Note that week(a)<=week(b) would not work, because weeks start again each year.
Note: There is more than one convention for counting calendar weeks (week starts on Sunday or on Monday, different start of first week of the year). You can pass an optional second parameter "mode" to week() to tell it which convention to use; see the docs. Of course for this problem only the start day of the week matters, not what the first week of the year is.

A more brute force method:
Select ...
From DateValues
Where date_a >= Date_Add( date_b, Interval -DayOfWeek( date_b ) + 1 Day )
And date_a <= Date_Add( date_b, Interval -DayOfWeek( date_b ) + 7 Day )

Related

Dynamic due dates checking with the given period of dates

id start_date interval period
1 1/22/2018 2 month
2 2/25/2018 3 week
3 11/24/2017 3 day
4 7/22/2017 1 year
5 2/25/2018 2 week
the above is my table data sample. start_dates will be expired based on interval and period(i.e id-1 will have due date after 2 months from the start_date, id-2 will have due after 3 weeks vice versa). period is enum of (day,week,month,year). Client can give any period of dates. let's say 25-06-2026 to 13-07-2026 like that.. I have to return the ids whose due dates falls under that period.I hope i made my question clear.
Here what i have done to resolve this. I am using mysql 5.7. I found ways to achieve this with recursive CTE's.(not available in mysql 5.7). and there is a way to achieve this by populating virtual records by using inline sub queries along with unions and its a performance killer and there is restriction of population of records.(like given in the link Generating a series of dates) I have reached a point to get results for a single date which is very easy. Below is my query(in oracle)
select id
from (select a.*,
case
when period='week'
then mod((to_date('22-07-2018','dd-mm-yyyy')-start_date),7*interval)
when period='month' and to_char(to_date('22-07-2018','dd-mm-yyyy'),'dd')=to_char(start_date,'dd')
and mod(months_between(to_date('22-07-2018','dd-mm-yyyy'),start_date),interval)=0
then 0
when period='year' and to_char(to_date('22-07-2018','dd-mm-yyyy'),'dd-mm')=to_char(start_date,'dd-mm')
and mod(months_between(to_date('22-07-2018','dd-mm-yyyy'),start_date)/12,interval)=0
then 0
when period='day'
and mod((to_date('22-07-2018','dd-mm-yyyy')-start_date),interval)=0
then 0 else 1 end filter from kml_subs a)
where filter=0;
But I need to do this for a period of dates not a single date. Any suggestions or solutions will be much appreciated.
Thanks,
Kannan
Assuming this is an Oracle question and not MySQL:
I think the first thing that you need to do is calculate when the due date is. I think a simple case statement can handle that for you:
case when period = 'day' then start_date + numtodsinterval(interval,period)
when period = 'week' then start_date + numtodsinterval(interval*7,'day')
when period = 'month' then add_months(start_date,interval)
when period = 'year' then add_months(start_date,interval*12)
end due_date
Then, using that new due_date field, you can check if the due date falls between the desired date range.
select *
from(
select id,
start_date,
interval,
period,
case when period = 'day' then start_date + numtodsinterval(interval,period)
when period = 'week' then start_date + numtodsinterval(interval*7,'day')
when period = 'month' then add_months(start_date,interval)
when period = 'year' then add_months(start_date,interval*12)
else null end due_date
from data)
where due_date between date '2018-02-25' and date '2018-03-12'
The above query checking between 2/25/18 and 3/12/18 produces the following output using your data:
+----+-------------+----------+--------+-------------+
| id | start_date | interval | period | due_date |
+----+-------------+----------+--------+-------------+
| 2 | 05-FEB-2018 | 3 | week | 26-FEB-2018 |
| 5 | 25-FEB-2018 | 2 | week | 11-MAR-2018 |
+----+-------------+----------+--------+-------------+

map sql column to date based on an end date

I have a pay period sybase table containing column names day01 - day14. I also have a column containing the pay period end date. Management is requesting the hours used for all the Mondays in the month of July.
How can I walk the column names mapping them to dates so I can get all Mondays in the month of July using sybase?
data example
Time_Sheet_Hours
day01 | day02 | day03 ... day14 | end_date
7.5 | 7.5 | 0 ... | 2017-07-05
day01 | day02 | day03 ... day14 | end_date
7.5 | 7.5 | 7.5 ... | 2017-07-19
day01 | day02 | day03 ... day14 | end_date
7.5 | 7.5 | 7.5 ... | 2017-08-02
Can you somehow do a nested query where the from could be dynamic based on the column sequence and mapped to a date based on the end_date?
You can use datepart in Sybase, or dayofweek in mysql, to determine the numeric day of the week (Sunday is 1) that end_date falls on. Then you can determine how many more days until "day 2" (Monday - not your "day02" column). Then either use a case statement, or execute a dynamic query, in order to sum up the correct day.
Here is an example in Sybase. It assumes that day14 is also the end_date.
Therefore, if end_date is a Sunday, then that means day14 was also a Sunday, and day01 and day08 represent Mondays.
select
sum
(
case
when (datepart(dw, end_date) = 1) then day01 + day08 -- Ends on Sun
when (datepart(dw, end_date) = 7) then day02 + day09 -- Ends on Sat
when (datepart(dw, end_date) = 6) then day03 + day10 -- Ends on Fri
when (datepart(dw, end_date) = 5) then day04 + day11 -- Ends on Thu
when (datepart(dw, end_date) = 4) then day05 + day12 -- Ends on Wed
when (datepart(dw, end_date) = 3) then day06 + day13 -- Ends on Tue
when (datepart(dw, end_date) = 2) then day07 + day14 -- Ends on Mon
else 0
end
)
from my_table
Bit of brute case when should do it:
SELECT
CASE WHEN dayofweek(end_date - INTERVAL 13 day) = 2 THEN day01 END +
CASE WHEN dayofweek(end_date - INTERVAL 12 day) = 2 THEN day02 END +
... -- and so on. Writing this out on an iPad is a pain
CASE WHEN dayofweek(end_date - INTERVAL 1 day) = 2 THEN day13 END +
CASE WHEN dayofweek(end_date - INTERVAL 0 day) = 2 THEN day14 END
as sum_of_mondays
FROM time sheet
WHERE end_date BETWEEN x AND y
Do please examine this for real with your data; check that the dayofweek() calc on the adjusted date really does correctly identify the Mondays- I've no MySQL instance to try this on but the docs say that dayofweek returns 2 for a Monday, and I've assumed that end_date and day14 are the same day hence subrptracting 0 days from it. If your end_date is midnight after the day upon which day14's hours are worked, then you'll need to adjust the subtracts by one (I.e. Subs should start by subtracting 14, and finish by subtracting 1)
Although this doesn't specifically answer my original question, I'd like to share our end solution. The end solution was to just build a view to query against which contained normalized data.

How can I set UNIX_TIMESTAMP correctly on my local?

I have a table which has a column named date_time. It is containing a unix number of the time. Something like this:
// mytable
+----+------------+
| id | date_time |
+----+------------+
| 1 | 1464499385 | -- 19 days ago
+----+------------+
-- ^ these are based on current time which is 1464566088
Also here is my query:
SELECT id,
(CASE WHEN FROM_UNIXTIME(date_time) >= CURDATE() THEN 'today'
WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesteray'
WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
ELSE 'in last month or more'
END) as `range`
FROM mytable
WHERE 1;
The result of query above on local isn't the same as on fiddle.
on local:
As you see the result on local is yesterday and on fiddle is today. Why there is a different and how can I fix it?
Note: when I select UNIX_TIMESTAMP on local and on fiddle, there is a different.
SELECT UNIX_TIMESTAMP(); -- 1464566511 (on local)
SELECT UNIX_TIMESTAMP(); -- 1464562972 (on fiddle)
So how can I set identically?
Sql fiddle server is in a different time zone.
Take 'UTC - your timezone' and subtract that many interval hours from your timestamp
You can convert with the difference using convert_tz :
SELECT CONVERT_TZ(datetime,'+00:00','-10:00');
Convert _TZ MySql documents
You can Google time in UTC and take the difference or you can set global tinezone to your local some support on that

MySQL: need to calculate the last Friday of a month

I'm trying to solve a task: I have a table containing information about ships' battles. Battle is made of name and date. The problem is to get the last friday of the month when the battle occurred.
WITH num(n) AS(
SELECT 0
UNION ALL
SELECT n+1 FROM num
WHERE n < 31),
dat AS (
SELECT DATEADD(dd, n, CAST(battles.date AS DATE)) AS day,
dateadd(dd, 0, cast(battles.date as date)) as fight,
name FROM num,battles)
SELECT name, fight, max(day) FROM dat WHERE DATENAME(dw, day) = 'friday'
I thought there must be a maximum of date or something, but my code is wrong.
The result should look like this:
Please, help!!
P.S. DATE_FORMAT is not available
Possible problem: as spencer7593 noticed - and as I should have done and didn't - your original query is not MySQL at all. If you're porting a query that's OK. Otherwise this answer will not be helpful, as it makes use of MySQL functions.
The day you want is number 4 (0 being Sunday in MySQL).
So you want the last day of the month if the last day of the month is a 4; if the day of the month is a 5 you want a date which is 1 day earlier; if the day of the month is a 3 you want a date which is 1 day later, but that's impossible (the month ends), so you really need a date six days earlier.
This means that if the daynumber difference is negative, you want it modulo seven.
You can then build this expression (#DATE is your date; I use a fake date for testing)
SET #DATE='2015-02-18';
DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY);
It takes the last day of the month (LASTDAY(#DATE)), then it computes its weekday, getting a number from 0 to 6. Adds seven to ensure positivity after subtracting; then subtract the desired daynumber, in this case 4 for Friday.
The result, modulo seven, is the difference (always positive) from the last day's daynumber to the wanted daynumber. Since DATE_SUB(date, 0) returns the argument date, we needn't use IF.
SET #DATE='1962-10-20';
SELECT DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY) AS friday;
+------------+
| friday |
+------------+
| 1962-10-26 |
+------------+
Your query then would become something like:
SELECT `name`, `date`,
DATE_SUB(LAST_DAY(`date`),
INTERVAL ((WEEKDAY(LAST_DAY(`date`))+7-4))%7 DAY) AS friday
FROM battles;

Number of e.g. Mondays left in month

How do you most easily calculate how many e.g. Mondays are left in a month using MySQL (counting today)?
Bonus points for a solution that solves it for all days of the week in one query.
Desired output (run on Tuesday August 17th 2010):
dayOfWeek left
1 2 -- Sunday
2 2 -- Monday
3 3 -- Tuesday (yep, including today)
4 2 -- Wednesday
5 2 -- Thursday
6 2 -- Friday
7 2 -- Saturday
Create a date table that contains one row for each day that you care about (say Jan 1 2000 - Dec 31 2099):
create table dates (the_date date primary key);
delimiter $$
create procedure populate_dates (p_start_date date, p_end_date date)
begin
declare v_date date;
set v_date = p_start_date;
while v_date <= p_end_date
do
insert ignore into dates (the_date) values (v_date);
set v_Date = date_add(v_date, interval 1 day);
end while;
end $$
delimiter ;
call populate_dates('2000-01-01','2099-12-31');
Then you can run a query like this to get your desired output:
set #date = curdate();
select dayofweek(the_date) as dayOfWeek, count(*) as numLeft
from dates
where the_date >= #date
and the_date < str_to_date(period_add(date_format(#date,'%Y%m'),1),'%Y%m')
group by dayofweek(the_date);
That will exclude days of the week that have 0 occurrences left in the month. If you want to see those you can create another table with the days of the week (1-7):
create table days_of_week (
id tinyint unsigned not null primary key,
name char(10) not null
);
insert into days_of_week (id,name) values (1,'Sunday'),(2,'Monday'),
(3,'Tuesday'),(4,'Wednesday'),(5,'Thursday'),(6,'Friday'),(7,'Saturday');
And query that table with a left join to the dates table:
select w.id, count(d.the_Date) as numLeft
from days_of_week w
left outer join dates d on w.id = dayofweek(d.the_date)
and d.the_date >= #date
and d.the_date < str_to_date(period_add(date_format(#date,'%Y%m'),1),'%Y%m')
group by w.id;
i found something
according to this article "find next monday"
http://www.gizmola.com/blog/archives/99-Finding-Next-Monday-using-MySQL-Dates.html
SELECT DATE_ADD(CURDATE(), INTERVAL (9 - IF(DAYOFWEEK(CURDATE())=1, 8,
DAYOFWEEK(CURDATE()))) DAY) AS NEXTMONDAY;
what we need to do is calculate the days between end month and next Monday,
and divide in 7 .
update (include current day) :
so the result is like :
for Monday
SELECT CEIL( ((DATEDIFF(LAST_DAY(NOW()),DATE_ADD(CURDATE(),
INTERVAL (9 - IF(DAYOFWEEK(CURDATE())=1, 8, DAYOFWEEK(CURDATE()))) DAY)))+1)/7)
+ IF(DAYOFWEEK(CURDATE())=2,1,0)
for Tuesday :
SELECT CEIL( ((DATEDIFF(LAST_DAY(NOW()),DATE_ADD(CURDATE(),
INTERVAL (10 - IF(DAYOFWEEK(CURDATE())=1, 8, DAYOFWEEK(CURDATE()))) DAY)))+1)/7)
+ IF(DAYOFWEEK(CURDATE())=3,1,0)
Have a look at my responses to;
MySQL: Using the dates in a between condition for the results
and
Select all months within given date span, including the ones with 0 values
for a way I think would work nicely, similar to #Walker's above, but without having to do the dayofweek() function within the query, and possibly more flexible too. One of the responses has a link to a SQL dump of my table which can be imported if it helps!