SQL select date range by month and year - mysql

How do I select date range from this query?
E.g : From Month('2017-05-22') And Year(date1) = Year('2017-05-22')
to Month('2018-05-22') And Year(date1) = Year('2018-05-22')
My current query :
SELECT
date1
FROM
data
WHERE
Month(date1) = Month('2018-05-22') And Year(date1) = Year('2018-05-22')

I'd convert those literals to dates using str_to_date:
SELECT MONTH(date1)
FROM data
WHERE date1 BETWEEN STR_TO_DATE('2017-05-22', '%Y-%m-%d') AND STR_TO_DATE('2018-05-23', '%Y-%m-%d')
Note that between is inclusive on the first argument and exclusive on the second, so I bumped the day to the 23rd.

BETWEEN condition to retrieve values within a date range.
For example:
SELECT *
FROM order_details
WHERE order_date BETWEEN CAST('2014-02-01' AS DATE) AND CAST('2014-02-28' AS DATE);
This MySQL BETWEEN condition example would return all records from the order_details table where the order_date is between Feb 1, 2014 and Feb 28, 2014 (inclusive). It would be equivalent to the following SELECT statement:
SELECT *
FROM order_details
WHERE order_date >= CAST('2014-02-01' AS DATE)
AND order_date <= CAST('2014-02-28' AS DATE);

SELECT TO_CHAR(systemts, 'yyyy-mm') as yyyymm,
max(notenum) - min(notenum) + 1 as notenum_range
FROM your_table_name
WHERE systemts >= to_date('2018-01-01', 'yyyy-mm-dd') AND
systemts < to_date('2018-07-17', 'yyyy-mm-dd')
GROUP BY TO_CHAR(systemts, 'yyyy-mm');

DECLARE #monthfrom int=null,
#yearfrom int=null,
#monthto int=null,
#yearto int=null
/**Region For Create Date on Behalf of Month & Year**/
DECLARE #FromDate DATE=NULL
DECLARE #ToDate DATE=NULL
SET #FromDate=DateAdd(day,0, DateAdd(month, #monthfrom - 1,DateAdd(Year, #yearfrom-1900, 0)))
SET #ToDate=DateAdd(day,-1, DateAdd(month, #monthto - 0,DateAdd(Year, #yearto-1900, 0)))
/**Region For Create Date on Behalf of Month & Year**/
SELECT DISTINCT month ,Year FROM tbl_Att
WHERE (DATEADD(yy, year - 1900, DATEADD(m, Month - 1, 1 - 1)) BETWEEN CONVERT(DATETIME, #FromDate, 102) AND
CONVERT(DATETIME, #ToDate, 102))

Related

MySQL: Find birthdays between a date range, but ignoring the year

I'm trying to query for users with birthdays falling between a given date range.
The users table stores birthdays in a pair of int columns: dob_month (1 to 12) and dob_day (1 to 31). The date range I'm querying with is a pair of date-time strings, including the year.
Here's what I've got so far:
SELECT *
FROM `users`
WHERE DATE(CONCAT_WS('-', 2023, dob_month, dob_day)) BETWEEN '2023-03-01 00:00:00' AND '2023-03-31 23:59:59'
However, this doesn't work when the date range spans multiple years.
For example, 2023-12-15 00:00:00 and 2024-01-10 23:59:59.
How can I work around this? Thanks!
You can solve this by joining to a set of rows with individual dates.
Suppose you had another table called dates which had one row per day, spanning the whole range you need.
mysql> create table dates (date date primary key);
mysql> insert into dates(date)
with recursive cte as (
select '2023-01-01' as date
union
select cte.date + interval 1 day from cte where cte.date < '2025-01-01'
)
select * from cte;
Query OK, 732 rows affected (0.01 sec)
Now it's easy to query a subset of dates:
mysql> SELECT date
FROM dates
WHERE dates.date BETWEEN '2023-12-15 00:00:00' AND '2024-01-10 23:59:59';
...
27 rows in set (0.00 sec)
We create a sample user with a dob of January 3.
mysql> create table users ( id serial primary key, dob_month tinyint, dob_day tinyint);
mysql> insert into users set dob_month = 1, dob_day = 3;
You can join your users table to that subset of dates where the month and day match.
mysql> SELECT date FROM users JOIN dates
ON dob_month = MONTH(date) AND dob_day = DAY(date)
WHERE dates.date BETWEEN '2023-12-15 00:00:00' AND '2024-01-10 23:59:59';
+------------+
| date |
+------------+
| 2024-01-03 |
+------------+
In the below code, the logic is to convert dob_month and dob_day into a date and then do the comparison using BETWEEN operator.
Now the year value used for date conversion is based on the below logic :
Use the year value the same as that of "from date". If the date is less than the "from date", then push it to the next year. Use BETWEEN operator to check if the date is within the given date range. This logic is applied because to use BETWEEN operator the date has to be greater than or equal to the "from date" keeping month and day values intact.
Note Date_add(Date_add(Makedate(some_year_value, 1), INTERVAL (dob_month)-1 month), INTERVAL (dob_day)-1 day) is repeated 3 times. It is for creating a date out of the year, month, and day values.
SET #fromdate = date('2023-09-01 00:00:00');
SET #fromyear = year(#fromdate);
SET #todate = date('2024-02-28 23:59:59');
CREATE TABLE users
(
id SERIAL PRIMARY KEY,
dob_month TINYINT,
dob_day TINYINT
);
INSERT INTO users
SET dob_month = 2,
dob_day = 1;
SELECT *
FROM users
WHERE CASE
WHEN Date_add(Date_add(Makedate(#fromyear, 1),
INTERVAL (dob_month)-1 month),
INTERVAL (dob_day)-1 day) < #fromdate THEN
Date_add(Date_add(Makedate(#fromyear + 1, 1),
INTERVAL (dob_month)-1 month),
INTERVAL (dob_day)-1 day) BETWEEN #fromdate AND #todate
ELSE Date_add(Date_add(Makedate(#fromyear, 1),
INTERVAL (dob_month)-1 month),
INTERVAL (dob_day)-1 day) BETWEEN #fromdate AND #todate
end;
Psuedo code for understanding the crux:
SELECT *
FROM users
WHERE CASE
WHEN Date(from_date_year,dob_month,dob_day) < #fromdate THEN
Date(from_date_year,dob_month,dob_day) BETWEEN #fromdate AND #todate
ELSE Date(from_date_year + 1,dob_month,dob_day) BETWEEN #fromdate AND #todate
end;

MySQL - calculate start and end date of months

I have table months:
id name
=============
1 January
2 February
3 March
.. ........
I have the year stored in a variable:
SET #year = YEAR(CURDATE());
I now want 2 new columns: start_date and end_date - both of these columns will contain the start date and end date of the month based on the id and #year variable. This will be in the standard MySQL date column format. Currently I have this:
CONCAT(#year, '-', LPAD(months.id, 2, '0'), '-', '01') AS start_date,
CONCAT(#year, '-', LPAD(months.id, 2, '0'), '-', '31') AS end_date
This does work but is there a better/cleaner way? Is there a way to automatically get the actual last day of the month?
You can get the first day of the year using date() and strings. The rest can be done using date functions and operators:
select m.*,
date(concat(year(curdate()), '-01-01')) + interval (id - 1) month as month_start,
last_day(date(concat(year(curdate()), '-01-01')) + interval (id - 1) month) as month_end
from months m;
Here is a small db<>fiddle.

Return Monthly Data From Query Even When Month Does Not Exist In DataSet

I am a MS SQL Server guy, but am having to write some MySQL Queries. I am attempting to write a query that will show monthly sales data for a selected employee and if the employee has no sales data for that month show the month and a 0.
This is the query I have but it's returning NULL?
CREATE TABLE `saleamountbyemployee` (
`month_year` varchar(9) DEFAULT NULL,
`total_sales` int(11) NOT NULL,
`employee` char(17) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `saleamountbyemployee` (`month_year`,`total_sales`,`employee`) VALUES ('Feb 18','34512','James Jones');
INSERT INTO `saleamountbyemployee` (`month_year`,`total_sales`,`employee`) VALUES ('Feb 18','223','Sally Smith');
INSERT INTO `saleamountbyemployee` (`month_year`,`total_sales`,`employee`) VALUES ('Feb 18','22','James Jones');
WITH RECURSIVE
cte_months_to_pull AS (
SELECT DATE_FORMAT(#start_date, '%Y-%m-01') - INTERVAL #number_of_months MONTH AS month_to_pull
UNION ALL
SELECT month_to_pull + INTERVAL 1 MONTH
FROM cte_months_to_pull
WHERE month_to_pull < #start_date + INTERVAL #number_of_months - 2 MONTH
)
SELECT YRS.months_to_pull,T.employee,COALESCE(T.IA, 0) IA
FROM (SELECT DATE_Format(month_to_pull, '%b-%Y') months_to_pull
FROM cte_months_to_pull
ORDER BY months_to_pull
) AS YRS
LEFT JOIN (SELECT Date_format(month_year, '%b-%Y') AS `Month`
,employee,Sum(total_sales) AS IA
FROM saleamountbyemployee
WHERE employee = 'James Jones'
GROUP BY Date_format(month_year, '%b-%Y'), employee) T
ON YRS.months_to_pull = T.`Month`
order by month(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y')),YEAR(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y'))
EDIT
If I alter syntax to this:
SET #start_date = 'Jan 18';
SET #number_of_months = 12;
WITH RECURSIVE
cte_months_to_pull AS (
SELECT str_to_date(CONCAT(#start_date,' 01'), '%b %y %d') - INTERVAL #number_of_months MONTH AS month_to_pull
UNION ALL
SELECT month_to_pull + INTERVAL 1 MONTH
FROM cte_months_to_pull
WHERE month_to_pull < #start_date + INTERVAL #number_of_months - 2 MONTH
)
SELECT YRS.months_to_pull,T.employee,COALESCE(T.IA, 0) IA
FROM (SELECT DATE_Format(month_to_pull, '%b-%Y') months_to_pull
FROM cte_months_to_pull
ORDER BY months_to_pull
) AS YRS
LEFT JOIN (SELECT Date_format(month_year, '%b-%Y') AS `Month`
,employee,Sum(total_sales) AS IA
FROM saleamountbyemployee
WHERE employee = 'James Jones'
GROUP BY Date_format(month_year, '%b-%Y'), employee) T
ON YRS.months_to_pull = T.`Month`
order by month(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y')),YEAR(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y'))
I now get this error message:
Error Code: 1292. Incorrect datetime value: 'Jan 18'
This line in the CTE:
SELECT DATE_FORMAT(#start_date, '%Y-%m-01') - INTERVAL #number_of_months MONTH AS month_to_pull
The date format of '%Y-%m-01' doesn't match what's in the table. If you use 'Feb 18' as the value of the #start_date parameter, it will return NULL. The date_format you specified expects it to look like this:
SELECT DATE_FORMAT('2018-01-01', '%Y-%m-01') - INTERVAL 1 MONTH AS month_to_pull
Add ' 01' to the date, this will make it think it's the first of the month.
SELECT str_to_date(CONCAT(#start_date,' 01'), '%b %y %d') - INTERVAL #number_of_months MONTH AS month_to_pull
This will return the following if you use 'Feb 18' as the #start_date and 1 as the #number_of_months:
1/1/2018 12:00:00 AM
That VARCHAR data type and date format in the table is gonna bite you.
I am attempting to write a query that will show monthly sales data for a selected employee and if the employee has no sales data for that month show the month and a 0.
Assuming that you have data for some employee in each month, you can use conditional aggregation:
select month_year,
sum(case when employee = 'James Jones' then total_sales else 0 end) as monthly_sales
from saleamountbyemployee sae
group by month_year;

SQL: Query periods of time given date

I have a list of periods during a year, and they are the same every year. You can think of it as a Season. They have a startDate and a endDate.
Because there can be Seasons that leap each other, what I need to to is query all the matching Seasons given a date, no matter what year.
As an example:
Season1: from 1st of January to 10th of January
Season2: from 6th of January to 8th of January
Season3: from 11th of January to 20th of January
Given the date 7th of January, I'd need to retrieve the Season1 and Season2.
I've tried converting all dates to the same year, but It doesn't work when the Start Date of a season in "later" than the End Date (for example, there's a period starting on November and ending of February).
Thanks in advance for the help.
Edit, sample data:
StartDate EndDate SeasonId
2000-08-01 2000-08-31 4
2000-12-29 2000-01-02 3
2000-06-01 2000-07-30 3
2000-09-01 2000-09-30 3
2000-01-06 2000-01-08 3
2000-04-07 2000-04-17 3
2000-04-28 2000-05-01 3
2000-06-02 2000-06-05 3
2000-06-23 2000-06-25 3
2000-09-08 2000-09-11 3
2000-09-22 2000-09-25 3
2000-10-12 2000-10-15 3
2000-11-01 2000-11-05 3
2000-12-01 2000-12-10 3
2000-12-22 2000-12-26 3
2000-03-01 2000-05-31 2
2000-10-01 2000-10-31 2
2000-11-01 2000-02-28 1
And I'd need, for example, the season for the date 2000-02-08, and retrieve seasonId = 1, or the date 2000-10-13and retrive seasonId = 3, seasonId = 2
I would do it in 2 'options': (the following SQL assumes you already got rid of the year in the table, and left only month-date format. )
select ... from seasons s where
(s.startDate <= s.endDate and s.startDate <= #mydate and s.endDate >= #mydate) or
(s.startDate > s.endDate and s.startDate >= #mydate and s.endDate <= #mydate)
You could query like this for the Season1:
select * from myTable where (month(myDate) = 1 and DAY(myDate) between 1 and 10)
If you have a season in more than one month, like start date January 20th, and finish date Febrery 10th, you could query this way:
select * from myTable where (month(myDate) = 1 and DAY(myDate) >= 20) or (month(myDate) = 2 and DAY(myDate) <= 10)
UPDATED WITH YOUR UPDATE
It is a little bit tricky, but it should work...
select * from seasons_table
where cast(cast(day(myDate) as char) + '/' + cast(month(myDate) as char) + '/' + '2000' as date) between
cast(cast(day(StartDate) as char) + '/' + cast(month(StartDate) as char) + '/' + '2000' as date) and
cast(cast(day(EndDate) as char) + '/' + cast(month(EndDate) as char) + '/' + '2000' as date)
given tblSeason with columns Id, startdate, enddate and your date as #myDate you would query as
Select Id From tblSeason WHERE #myDate BETWEEN startdate AND enddate
would give list of Id's of the seasons that match.
if you can't work from that, please give more information in your examples as to the structure you are querying and the expected outcome.
*Edit to ignore the year part you could do similar to
Declare #myDate datetime = '2016-10-13'
SELECT [StartDate]
,[EndDate]
,[SeasonId]
FROM [dbo].[Table_1]
where DATEPART(dy, #myDate) >= DATEPART(dy,StartDate)
AND (DATEPART(dy,#myDate) =< DATEPART(dy,EndDate) OR DATEPART(dy,StartDate) > DATEPART(dy,EndDate))
Why are you including the year in the table? That seems strange.
In any case, you only care about the MM-DD format, so use date_format() to convert the values to strings:
select t.*
from t
where (start_date <= end_date and
date_format(#date, '%m-%d') >= date_format(start_date, '%m-%d') and
date_format(#date, '%m-%d') <= date_format(end_date, '%m-%d')
) or
(start_date > end_date and
date_format(#date, '%m-%d') <= date_format(start_date, '%m-%d') and
date_format(#date, '%m-%d') >= date_format(end_date, '%m-%d')
);
The strings are fine for comparison, because you are only looking at the month and day components of the date.
Given the nature of your problem, I would recommend that you store start_date and end_date in a non-date format, such as MM-DD.

How to add hours to current date in SQL Server?

I am trying to add hours to current time like
-- NOT A VALID STATEMENT
-- SELECT GetDate(DATEADD (Day, 5, GETDATE()))
How can I get hours ahead time in SQL Server?
DATEADD (datepart , number , date )
declare #num_hours int;
set #num_hours = 5;
select dateadd(HOUR, #num_hours, getdate()) as time_added,
getdate() as curr_date
Select JoiningDate ,Dateadd (day , 30 , JoiningDate)
from Emp
Select JoiningDate ,DateAdd (month , 10 , JoiningDate)
from Emp
Select JoiningDate ,DateAdd (year , 10 , JoiningDate )
from Emp
Select DateAdd(Hour, 10 , JoiningDate )
from emp
Select dateadd (hour , 10 , getdate()), getdate()
Select dateadd (hour , 10 , joiningDate)
from Emp
Select DateAdd (Second , 120 , JoiningDate ) , JoiningDate
From EMP
The DATEADD() function adds or subtracts a specified time interval from a date.
DATEADD(datepart,number,date)
datepart(interval) can be hour, second, day, year, quarter, week etc;
number (increment int);
date(expression smalldatetime)
For example if you want to add 30 days to current date you can use something like this
select dateadd(dd, 30, getdate())
To Substract 30 days from current date
select dateadd(dd, -30, getdate())
declare #hours int = 5;
select dateadd(hour,#hours,getdate())
SELECT GETDATE() + (hours / 24.00000000000000000)
Adding to GETDATE() defaults to additional days, but it will also convert down to hours/seconds/milliseconds using decimal.
If you are using mySql or similar SQL engines then you can use the DATEADD method to add hour, date, month, year to a date.
select dateadd(hour, 5, now());
If you are using postgreSQL you can use the interval option to add values to the date.
select now() + interval '1 hour';