MySQL date_add behaviour - mysql

I've written some SQL to give me a range of dates between two times like so:
select date_add(x.min_date, interval ((t500.id-1) * 30) minute) period
from (
select '2013-08-05T23:00' as min_date, '2013-08-06T01:00' as max_date
) x,
t500
where date_add(x.min_date, interval ((t500.id-1) * 30) minute) <= x.max_date);
Where T500 is a trivial table with column id of 1 to 500 I use for simulating a loop.
Now I expect this to return:
2013-08-05 23:00:00
2013-08-05 23:30:00
2013-08-06 00:00:00
2013-08-06 00:30:00
2013-08-06 01:00:00
and finish there. But instead it carries on until 2013-08-06 23:30:00. I tried different max dates and it always returns dates to the end of the day. Could someone explain what's happening and how to make it stop when I want?

First thing that comes to mind would be casting your date strings into a date format instead of a string for example:
cast('2013-08-05T23:00' as smalldatetime)

Related

Getting timestamp of first day of month

DB-Fiddle
CREATE TABLE PaL (
id SERIAL PRIMARY KEY,
event_date DATE
);
INSERT INTO PaL
(event_date)
VALUES
('2020-01-01'),
('2020-02-05'),
('2020-03-20'),
('2020-04-15'),
('2020-05-11'),
('2020-06-18'),
('2020-07-19'),
('2020-12-31');
Expected Result:
first_date_of_the_month first_timestamp_of_the_month
2020-01-01 2020-01-01 00:00:00
2020-02-01 2020-02-01 00:00:00
2020-03-01 2020-03-01 00:00:00
2020-04-01 2020-04-01 00:00:00
2020-05-01 2020-05-01 00:00:00
2020-06-01 2020-06-01 00:00:00
2020-07-01 2020-07-01 00:00:00
2020-12-01 2020-12-01 00:00:00
I want to extract the first date and first timestamp of each event_date in the table.
I am doing this with the below query:
SELECT
DATE_ADD(DATE_ADD(LAST_DAY(pl.event_date), INTERVAL 1 DAY), INTERVAL -1 MONTH) AS first_date_of_the_month,
DATE_FORMAT(DATE_ADD(DATE_ADD(LAST_DAY(pl.event_date), INTERVAL 1 DAY), INTERVAL -1 MONTH), '%Y-%m-%d %H:%i:%s') AS first_timestamp_of_the_month
FROM PaL pl
However, HeidiSQL is somehow interpeting the timestamp as value and not as a TIMESTAMP format.
How do I need to change the query so it displays the result as TIMESTAMP?
SELECT *,
DATE_FORMAT(event_date, '%Y-%m-01') AS first_date_of_the_month,
DATE_FORMAT(event_date, '%Y-%m-01 00:00:00') AS first_timestamp_of_the_month
FROM PaL
HeidiSQL is somehow interpeting the timestamp as value and not as a TIMESTAMP format.
This is client problem.
MySQL's datatype system is soft, i.e. each value is converted to needed datatype according to current datatype context implicitly. But you may use correct final context or excplicit final CAST.
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=21a7d3fccaca1329ef8e1797d366c5a6
You don't have to convert to strings to accomplish this. It is simple enough with basic date functions:
SELECT event_date + interval (1 - day(event_date)) day as first_date_of_the_month,
timestamp(event_date + interval (1 - day(event_date)) day) as first_timestamp_of_the_month
FROM PaL pl;
Here is a db<>fiddle.
Gotta chime in here.... The way I prefer in MySQL, the way that uses the inbuilt LAST_DAY() function, is this:
SELECT LAST_DAY(event_date) + INTERVAL 1 DAY - INTERVAL 1 MONTH
For what it's worth the string constants 2021-03-13 00:00:00 and the shorter 2021-03-13 have precisely the same meaning when used as DATE, DATETIME, or TIMESTAMP values. There's little need for a separate first_timestamp_of_the_month value.
It works in Oracle too. In SQL Server, it's EOMONTH(). postgreSQL, not so much.

SQL - Last Day of Month

I got a Table which looks like this:
DATE | Number
01-01-16 00:00:00 10
02-01-16 00:00:00 10
03-01-16 00:00:00 11
04-01-16 00:00:00 12
05-01-16 00:00:00 13
....
31-01-16 00:00.00 15
........
29-02-16 00:00:00 18
I got this table for the last few months.
I now want to retrieve the value of the rows, which contain the last day of the previous month and the month before the last month. So for today I would like to retrieve the Value of the 31-1-16 and 29-2-16.
My result should look like:
lastmonth | lastmonth2
18-> Corresponding value to Date: 29-02-16 | 15 -> value for 31-01-16
Would appreciate any help.
Cheers
Here is logic for the last day of this month and the previous month:
select last_day(curdate()) as last_day_of_this_month,
last_day(date_sub(curdate(), interval 1 month)) as last_day_of_prev_month
You can get the last day of any month relative to the current month by changing the "1".
And, I have no idea what date "30-2-16". When describing dates, you should use ISO standard formats. The last day of February 2016 was 2016-02-29.
This is Gordon's code for determining the correct dates plus subqueries to fetch the Number values for those rows:
SELECT
(SELECT Number FROM cc_open_csi_view
WHERE last_day(date_sub(curdate(), interval 1 month)) = date(`DATE`)) as lastmonth,
(SELECT Number FROM cc_open_csi_view
WHERE last_day(date_sub(curdate(), interval 2 month)) = date(`DATE`)) as lastmonth2
FROM DUAL;
Hope that's what you wanted! Works for me in a simple example. I don't know if you need the date() part around DATE but it seemed safest.
SELECT CASE
WHEN last_day(curdate()) = `DATE` THEN number
END as number_last_month,
CASE
WHEN last_day(date_sub(curdate(), interval 1 month)) = `DATE`
THEN number
END as number_last_month2
FROM cc_open_csi_view
I can't test it right now on sqlfiddle.

Get average day or week values

I have statistical data like this:
time val1
1424166578 51
1424166877 55
1424167178 57
1424167477 57
time is a unix timestamp. There is one record every 5 minutes excluding nights and sundays. This continues over several weeks.
Now I want to get these values for an average day and an average week. The result should include values for every 5 minutes like normal but for average past days or weeks.
The result should look like this:
time val1
0 43.423
300 46.635
600 51.887
...
So time could be a timestamp with relative time since day or week start. Perhaps it is better to use DATETIME... not sure.
If I use GROUP BY FROM_UNIXTIME(time, '%Y%m%d') for example I get one value for the whole day. But I want all average values for all days.
You seem to be interested in grouping dates by five minute intervals instead of dates. This is fairly straightforward:
SELECT
HOUR(FROM_UNIXTIME(time)) AS HH,
(MINUTE(FROM_UNIXTIME(time)) DIV 5) * 5 AS MM,
AVG(val1) AS VAL
FROM your_table
WHERE time > UNIX_TIMESTAMP(CURRENT_TIMESTAMP - INTERVAL 7 DAY)
GROUP BY HH, MM
The following result will explain how date is clamped:
time FROM_UNIXTIME(time) HH MM
1424166578 2015-02-17 14:49:38 14 45
1424166877 2015-02-17 14:54:37 14 50
1424167178 2015-02-17 14:59:38 14 55
1424167477 2015-02-17 15:04:37 15 00
I would approach this as:
select date(from_unixtime(time)) as day, avg(val)
from table t
group by date(from_unixtime(time))
order by day;
Although you can use the format argument, I think of that more for converting the value to a string than to a date/time.

MYSQL Queries to find immediate next DateTime

I was writing a mini scheduler that perform certain task.
For calculating trigger time, I am using MYSQL. I am stucked at writing one of the query.
Find immediate DateTime which is greater than the given prevtime,
AND
the Day of the required immediate datetime should be ANY of given days
AND
time(HH:MM:SS) portion of required immediate datetime should be equal to given time.
Examples:
(a)
If given days are ('MON', 'WEDNES', 'SAT'),
given time is 10:15:00,
given prevtime is 2014-11-12 23:17:00
Then MYSQL should return
2014-11-15 10:15:00
(b)
Given Days: ('SUN','SAT','TUES')
Given Time: 09:10:00
Given prevtime is 2014-11-30 07:05:12
MYSQL should return 2014-11-30 09:10:00
(c)
Given Days: ('MON','THURS','SAT')
Given Time: 11:00:00
Given prevtime is 2014-12-29 11:55:12
MYSQL should return 2015-01-01 11:00:00
(d)
Days: (SUN, THURS, SAT)'
Given prevtime is 2014-02-27 18:15:00
Given Time 15:15:00
MYSQL Query result: 2014-03-01 15:15:00
(e)
DAYS: (TUES, WED, FRI)
Prev Date: 2014-12-23 09:30:00
Time : 08:00:00
Expected Result:
2014-12-24 08:00:00
(f)
DAYS: SUN, TUES, THURS
Prev Date: 2014-07-31 10:10:00
Time: 06:07:08
Expected Res:
2014-08-03 06:07:08
Using numeric weekday numbers, 0=Monday, 6=Sunday:
set #day1=0;
set #day2=2;
set #day3=5;
set #time=time('10:15:00');
set #prevtime=timestamp('2014-11-12 23:17:00');
select if(weekday(#nexttime:=date_add(concat(date(#prevtime),' ',#time),interval if(#time>time(#prevtime),0,1) day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,date_add(#nexttime,interval 1 day))))))) as nexttime;
If you have only one weekday, you can set all three variables to the same number.
You should be able to formulate the where clause using the DAYNAME(), HOUR(), MINUTE() and SECOND() functions:
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html
If performance is inadequate and you start wishing you could index on DAYNAME(columname) for example, you can consider denormalizing your data and storing the DAYNAME value separately.
It might be simpler to switch to Postgres at that point though:
http://www.postgresql.org/docs/9.1/static/indexes-expressional.html

Mysql query for group by month

I am looking to do something like get all rows from table
where date >='2012-05-05' and date<='2012-07-20'
I want MySQL to return "group by" rows mont wise incremented from
2012-05-05 to 2012-06-05(incerement 1 month)
2012-06-06 to 2012-07-06(increment 1` month)
and remaining 2012-07-07 to 2012-07-20 (group the remaining 14 days)
how can i write my query to achieve the same?
Thank you...
Try this solution:
You can GROUP BY the number of months elapsed from your parameter minimum (2012-05-05) + 1 to the date in each row via the TIMESTAMPDIFF() function:
GROUP BY TIMESTAMPDIFF(MONTH, '2012-05-05' + INTERVAL 1 DAY, date)
The reason why we +1 day to your minimum parameter is because:
2012-05-05 to 2012-06-04 is 0 months, but...
2012-05-05 to 2012-06-05 is 1 month
^ Because of that, the row(s) on 2012-06-05 would be grouped separately from dates that had 0 months elapsed when we actually want it grouped WITH them.
Edit: I was fiddling around with this solution not only grouping by the month intervals, but also displaying the from and to dates of each interval.
Check it out:
SQLFiddle Demo
You could use the case expression and then group by on the result of case.
SELECT
CASE
WHEN where date >='2012-05-05' and date<='2012-06-05' THEN 1
WHEN where date >='2012-06-06' and date<='2012-07-06' THEN 2
ELSE 3
END AS period, COUNT(*)
FROM your_table
GROUP BY period
Hope this help, I assumed the date range is dynamic like this :
2012-01-01 to 2012-02-01
2012-02-02 to 2012-03-02
2012-03-03 to 2012-04-03
2012-04-04 to 2012-05-04
2012-05-05 to 2012-06-05
...
So, I can group it using :
SELECT *, IF(day(date)>=month(date), month(date), month(date)-1) as PERIOD
FROM your_tablename
GROUP BY PERIOD;