Mysql getting certian day's date with date range (syntax error) - mysql

This is my full code but when i start it,
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
SET #StartDateTime = '2022-04-01'
SET #EndDateTime = '2022-04-29';
WITH DateRange(Dates, DateWD) AS
(
SELECT #StartDateTime as Date, DATEPART(WEEKDAY, #StartDateTime)
UNION ALL
SELECT DATEADD(d,1,Dates), DATEPART(WEEKDAY, DATEADD(d,1,Dates))
FROM DateRange
WHERE Dates < #EndDateTime
)
SELECT Dates, DateWD
FROM DateRange
WHERE DATEWD NOT IN(1,7) AND Dates NOT IN(
SELECT (HOLI_YEAR + '-' + HOLI_MONTH + '-' + HOLI_DAY) AS DATE
FROM TB_HOLIDAY_CODE
OPTION (MAXRECURSION 0)
This error cames out.
I want to show 2022, april's date list except SUNDAY and SATURDAY
eg) start date is 2022-04-01
end date is 2022-04-30
so result come out ->
Dates
DateWD
2022-04-01
(FRI)
2022-04-04
(MON)
2022-04-05
(TUE)
2022-04-06
(WED)
2022-04-07
(THU)
2022-04-08
(FRI)
2022-04-11
(MON)
....
...
How can i fix this code? help me please. Thank you
*** I will not use table. Because i don't have table, and i want to use only SQL QUERY.

The syntax error is because that example is written for SQL Server, not MySQL. It needs a few adjustments to work with MySQL 8.x:
You don't need to DECLARE the user defined variables. Just use SET to both declare and assign the variable values
DATEADD() is a SQL Server function. The MySQL equivalent is DATE_ADD(date,INTERVAL expr unit)
DATEPART(weekday,...) is a SQL Server function. For MySQL, try DAYOFWEEK(date)
Lastly, use the keyword RECURSIVE with the CTE. From the docs:
A common table expression is recursive if its subquery refers to its
own name. The RECURSIVE keyword must be included if any CTE in the
WITH clause is recursive.
SQL
SET #StartDateTime = '2022-04-01';
SET #EndDateTime = '2022-04-29';
WITH RECURSIVE DateRange(Dates, DateWD) AS
(
SELECT #StartDateTime, DayOfWeek(#StartDateTime)
UNION ALL
SELECT DATE_ADD(Dates, INTERVAL 1 DAY), DayOfWeek(DATE_ADD(Dates, INTERVAL 1 DAY))
FROM DateRange
WHERE Dates < #EndDateTime
)
SELECT *
FROM DateRange
WHERE DateWDNOT IN(1,7)
Result:
Dates
DateWD
2022-04-01
6
2022-04-04
2
2022-04-05
3
2022-04-06
4
2022-04-07
5
2022-04-08
6
2022-04-11
2
2022-04-12
3
2022-04-13
4
2022-04-14
5
2022-04-15
6
2022-04-18
2
2022-04-19
3
2022-04-20
4
2022-04-21
5
2022-04-22
6
2022-04-25
2
2022-04-26
3
2022-04-27
4
2022-04-28
5
2022-04-29
6
db<>fiddle here

Related

How to showing date on mysql 5.7 from first date of month until end date of month on MYSQL 5.7? [duplicate]

This question already has answers here:
Get a list of dates between two dates
(23 answers)
Closed 1 year ago.
i want to ask how to showing the date from first date of the month until end of date of the month
for example i want to showing date from 2021-01-01 until 2021-01-30. i want join this data with my other table.
iam using MYSQL V5.7
i want this result ::
| 2019-01-01 |
| 2019-01-02 |
| 2019-01-03 |
| 2019-01-04 |
| 2019-01-05 |
| .......... |
| .......... |
| 2019-01-31 |
i try using this Query but doesn't work
SELECT DATE(DATE_SUB(CURRENT_DATE, INTERVAL DAYOFMONTH(CURRENT_DATE)-1 DAY) BETWEEN DATE(NOW()))
Thanks for the answer
If your question is how to return all dates for a single month, you can use a recursive CTE:
with recursive dates as (
select date('2021-01-01') as dte
union all
select dte + interval 1 day
from dates
where dte < last_day(dte)
)
select *
from dates;
This can easily be extended to other date ranges by changing the where condition.
Here is a db<>fiddle.
Old versions of MySQL cannot generate rows that are not stored in a table. I always find myself building a "dates" table to get this done.
You can use this process:
DELIMITER $$
CREATE PROCEDURE fill_dates_table(start_date DATE, end_date DATE)
BEGIN
DECLARE d DATE;
CREATE TABLE IF NOT EXISTS dates (
date DATE NOT NULL PRIMARY KEY
);
SET d = start_date;
WHILE d <= end_date DO
INSERT IGNORE INTO dates VALUES (d);
SET d = d + INTERVAL 1 DAY;
END WHILE;
END $$
DELIMITER ;
Execute this procedure:
CALL fill_dates_table('2019-01-01', '2019-01-31');
Then, use the dates table as you see fit.
If you're using version 8, consider Gordon's answer.

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.

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 date_add behaviour

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)

Grouping records hour by hour or day by day and filling gaps with zero or null in mysql

I have written a query that counts records hour by hour:
select TO_CHAR(copied_timestamp, 'YYYY-MM-DD HH24'),count(*) from req group by
TO_CHAR(copied_timestamp, 'YYYY-MM-DD HH24');
the result is:
2012-02-22 13 2280
2012-02-22 15 1250
2012-02-22 16 1245
2012-02-22 19 1258
But I need a result like this:
2012-02-22 13 2280
2012-02-22 14 0
2012-02-22 15 1250
2012-02-22 16 1245
2012-02-22 17 0
2012-02-22 18 0
2012-02-22 19 1258
Also I have these queries that group by day and month too!
select TO_CHAR(copied_timestamp, 'YYYY-MM-DD'),count(*) from req
group by TO_CHAR(copied_timestamp, 'YYYY-MM-DD');
select TO_CHAR(copied_timestamp, 'YYYY-MM'),count(*) from req
group by TO_CHAR(copied_timestamp, 'YYYY-MM');
I need their gaps to be filled with zero or null too.
Any help is really appreciated.
Note:
There is an answer for this question in oracle using CONNECT BY but i need the answer in Mysql because Mysql does not support CONNECT BY.
Here is the link
Generate a single-column dates_hours table which contains all dates and hours within a reasonable range (e.g. from 1900 to 2200).
Then do a LEFT JOIN from this table to your current query.
For this technique to perform correctly, you will probably need to add an indexed column to your table which contains a converted time stamp (your copied_timestamp converted to DATETIME, rounded to the hour)
SELECT date_hour, count(req.converted_timestamp)
FROM
dates_hours
LEFT JOIN req ON req.converted_timestamp = dates_hours.date_hour
WHERE date_hour
BETWEEN (SELECT MIN(req.converted_timestamp) FROM req)
AND (SELECT MAX(req.converted_timestamp) FROM req)
GROUP BY date_hour
To generate the dates_hours table:
CREATE TABLE dates_hours (date_hour DATETIME PRIMARY KEY);
DELIMITER $$$
CREATE PROCEDURE generate_dates_hours (to_date DATETIME)
BEGIN
DECLARE start_date DATETIME;
DECLARE inc INT;
SELECT MAX(date_hour) INTO start_date FROM dates_hours;
IF start_date IS NULL THEN
SET start_date = '1900-01-01';
END IF;
SET inc = 1;
WHILE start_date + INTERVAL inc HOUR <= to_date DO
INSERT INTO dates_hours VALUE (start_date + INTERVAL inc HOUR);
SET inc = inc +1;
END WHILE;
END $$$
DELIMITER ;
CALL generate_dates_hours('2200-01-01');
Okay, now that I am proof-reading myself, I realise this is quite far-fetched a solution. I hope someone comes up with a more elegant one.
I created a table called TBL_NUMBERS
CREATE TABLE `TBL_NUMBER` (`n` int(11) NOT NULL)
and inserted records from 1 to 1000.
Now I can generate any kind of date range using this query:
SELECT '2012-06-21' + INTERVAL n-1 [DAY | HOUR | MINUTE] or as dateRange
FROM TBL_NUMBER
WHERE '2012-06-21' + INTERVAL n-1 [DAY | HOUR | MINUTE] <= '2012-06-23';
Then I can join this table with my results to fill the date gap.
If i need more than 1000 date range I can insert more records in TBL_NUMBER
If you have any better idea, I'm eager to know that ;)