Given:
Start Year
Start Month & Start Day
End Month & End Day
What SQL statement results in TRUE if a date lands between the Start and End days? The problem is in finding the end year so as to get a start_date and an end_date.
Maybe convert the dates to UNIX timestamps? Then it would be simple less-than or greater-than integer arithmetic.
Do you really need true/false values, or can you just SELECT? Assuming $date contains the date you're looking for in 'yyyy-mm-dd' format:
SELECT * FROM mytable WHERE $date > start_date AND date < end_date;
Having the year in a separate field also works, but looks uglier and probably kills performance:
SELECT * FROM mytable WHERE $date > CONCAT(year, '-', start_date) AND
$date < CONCAT(year, '-', end_date);
And with handling for the end < start case:
SELECT * FROM mytable WHERE $date > CONCAT(year, '-', start_date) AND $date <
IF(start_date < end_date,
CONCAT(year, '-', end_date),
DATE_ADD(CONCAT(year, '-', end_date), INTERVAL 1 YEAR));
One way to calculate the end year (so as to derive the end_date):
end_year = start_year +
greatest( -1 *
sign(
datediff(
date(
concat_ws('-', year, end_month, end_day )
),
date(
concat_ws('-', year, start_month, start_day )
)
)
), 0
)
Related
I have this query :
SELECT * FROM tabl1
WHERE
(
created_time BETWEEN
DATE_FORMAT(NOW() ,'%Y-%m-02')
AND
DATE_FORMAT(NOW() ,'%Y-%m-02')+INTERVAL 1 MONTH
AND
DAY(NOW()) > 1
)
OR
(
created_time BETWEEN
DATE_FORMAT(NOW() ,'%Y-%m-02')
AND
DATE_FORMAT(NOW() ,'%Y-%m-02')-INTERVAL 1 MONTH
AND
DAY(NOW()) = 1
)
this query will return the columns that created last month,
but the month start at day number 2 and end at the second day of the next month !
so it will return :
3.3.2015
2.3.2015
but it will not return
1.3.2015
How to write the query with better way ?
Always do this:
where datefield >= YourStartDate
and datefield < TheDayAfterYourEndDate
Dateformat returns strings and is for display purposes only.
I think you can write the query like that:
SELECT *
FROM tabl1
WHERE DATE >= '2015-02-02 00:00' AND DATE_2 <= '20015-03-02 23:59'
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';
I'm trying to do a query search for closing hours of stores.
The issue is that since some venues close at 2,3,4 am - how can I do a query of where LESS THAN or EQUAL when a table row value of 04:00 means more to me than 23:00?
Thanks!
Since the closing time has to happen after the opening time, if closing_time is less than opening_time that means that the interval rolls over the next day.
How to check if a given time is inside an interval? A given time is inside an interval if:
given_time is between opening_time and closing_time, when interval doesn't roll over;
given_time is NOT between opening_time and closing_time, when closing time is on the next day.
As far as I remember, I think I've never used XOR in a SQL Query before, but here it fits perfectly:
closing_time<opening_time
XOR
(time(now()) >= least(opening_time,closing_time) and
time(now()) < greatest(opening_time, closing_time))
this gives true if time(now()) is inside any interval, even if closing_time is less than opening_time. Notice that I had to use >= and <, using >= and <= (or its equivalent BETWEEN) won't always give the correct result, but I believe the logic is correct.
If we have to check if time(now()) is inside an interval of a specific dat, we still have a problem: if a store opens on Monday, and closes at 1AM, closing time is not on Monday anymore, so if we are past midnignt, and the interval is swapped, we have to check the interval of the previous day. This checks the correct opening_day:
opening_day =
date_format(now() - INTERVAL
(closing_time<opening_time and time(now())<closing_time) DAY, '%a'))
if closing_time rolls on the next day, and current time is before closing time, the condition in () will be true so we have to check INTERVAL 1 DAY, otherwise condition is false and subtracting INTERVAL 0 DAY gives the current day.
So my final query is this:
set #n='2013-01-11 18:15:00';
SELECT
e.name,
hrs.opening_day,
hrs.opening_time,
hrs.closing_time
FROM
hours_of_operation hrs join establishments e
on hrs.establishment_id = e.id
and not is_closed
and ((hrs.opening_day=date_format(#n -
INTERVAL
(closing_time<opening_time and time(#n)<closing_time)
DAY, '%a'))
AND (closing_time<opening_time XOR
(time(#n) >= least(opening_time,closing_time)
and time(#n) < greatest(opening_time, closing_time))))
;
that gives all stores open at the given #n datetime. Fiddle is here.
EDIT:
I had another (maybe weird?) idea to solve the same problem. It should be a little easier to understand. The first day of 2001 is '2001-01-01' which is a date that looks nice, and has also the interesting property of being a Monday. So my idea is to shift all dates (current date, opening date, closing date) back to this date.
Current date would then become:
'2001-01-01'
+ INTERVAL WEEKDAY( #n ) DAY
+ INTERVAL TIME_TO_SEC( #n ) SECOND
(we add the day of the week, weekday of a monday is 0 so we will still be on '2001-01-01', tuesday is 1 so it will be '2001-01-02' and so on). And using TIME_TO_SEC I'm adding the second to make a datetime field.
Opening date would be:
'2001-01-01'
+ INTERVAL
FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')-1
DAY
+ INTERVAL TIME_TO_SEC(opening_time) SECOND
to convert opening_day to the number of days to add I'm using FIELD function, then we have to subtract 1 since Monday starts to 1 here, and then we add the time part.
And the closing date would be:
'2001-01-01'
+ INTERVAL
FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')
-(closing_time>opening_time) DAY
+ INTERVAL TIME_TO_SEC(closing_time) SECOND
same as opening date, but I'm subtracting 1 to DAY only opening_time is before closing_time, otherwise it means that closing_time happens on the next day. Then we only have to use BETWEEN to check if we are inside the interval, like this:
SELECT
establishments.id,
establishments.name,
opening_time,
closing_time
FROM
`hours_of_operation` inner join establishments
on hours_of_operation.establishment_id=establishments.id
and not is_closed
and
'2001-01-01'
+ INTERVAL WEEKDAY( #n ) DAY
+ INTERVAL TIME_TO_SEC( #n ) SECOND
BETWEEN
'2001-01-01'
+ INTERVAL
FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')-1
DAY
+ INTERVAL TIME_TO_SEC(opening_time) SECOND
AND
'2001-01-01'
+ INTERVAL
FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')
-(closing_time>opening_time) DAY
+ INTERVAL TIME_TO_SEC(closing_time) SECOND
Fiddle here.
But at this point, we can just convert to seconds since the beginning of the week, we have all elements to do this, and my final query would be this:
SELECT
establishments.id,
establishments.name,
opening_time,
closing_time
FROM
`hours_of_operation` inner join establishments
on hours_of_operation.establishment_id=establishments.id
and not is_closed
and
WEEKDAY( #n ) * 86400 + TIME_TO_SEC( #n )
BETWEEN
(FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')-1)*86400+
TIME_TO_SEC(opening_time)
AND
(FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')
-(closing_time>opening_time))*86400
+ TIME_TO_SEC(closing_time)
I think you should use datetime,timestamp or time as datatype. As it will be usefull in future for mathamatical calculations.
If you store it as string or number it will be difficult you to do something on it.
So be specific while choosing datatype.
Pick an arbitrary date for storing opening and closing times, say Jan 1st 1900.
Any store that opens and closes on the same day uses this date for both times.
So a store that opens at 9am and closes at 5pm gets:
Open: 1900-01-01 09:00:00
Close: 1900-01-01 17:00:00
A store that opens one day and closes the next uses the following day for its closing time.
So a bar that opens at 4pm and closes at 2am the following day gets:
Open: 1900-01-01 16:00:00
Close: 1900-01-02 02:00:00
Now you can use greater than and less than queries and get the results you expect.
You have to check if closing_time is less than opening_time to know that this is meant to be the next day.
To get the valid opening time for a given datetime you also have to check if the given datetime is less than opening time to create the correct time interval.
To get the opening times for a given datetime use
SET #MyDate = Now();
SELECT
title,
CAST(
CONCAT(
CASE
WHEN TIME( #MyDate ) < opening THEN
DATE( #MyDate ) - INTERVAL 1 DAY
ELSE DATE( #MyDate )
END, ' ', opening
) AS DATETIME
) open_from,
CASE
WHEN opening > closing THEN
CAST(
CONCAT(
CASE
WHEN TIME( #MyDate ) < opening THEN
DATE( #MyDate ) - INTERVAL 1 DAY
ELSE DATE( #MyDate )
END + INTERVAL 1 DAY, ' ', closing
) AS DATETIME
)
ELSE
CAST(
CONCAT(
CASE
WHEN TIME( #MyDate ) < opening THEN
DATE( #MyDate ) - INTERVAL 1 DAY
ELSE DATE( #MyDate )
END, ' ', closing
) AS DATETIME
)
END open_till
FROM
stores
ORDER BY
open_from;
To get the actual open/closed state use
SET #MyDate = Now();
SELECT
title,
CASE
WHEN
#MyDate >= CAST( CONCAT( CASE
WHEN TIME( #MyDate ) < opening THEN
DATE( #MyDate ) - INTERVAL 1 DAY
ELSE DATE( #MyDate )
END, ' ', opening ) AS DATETIME )
AND
#MyDate < CASE
WHEN opening > closing
THEN CAST( CONCAT( CASE
WHEN TIME( #MyDate ) < opening THEN
DATE( #MyDate ) - INTERVAL 1 DAY
ELSE DATE( #MyDate )
END + INTERVAL 1 DAY, ' ', closing ) AS DATETIME )
ELSE CAST( CONCAT( CASE
WHEN TIME( #MyDate ) < opening THEN
DATE( #MyDate ) - INTERVAL 1 DAY
ELSE DATE( #MyDate )
END, ' ', closing ) AS DATETIME )
END
THEN 'open'
ELSE 'closed'
END state
FROM
stores;
SQL Fiddle DEMO
UPDATE
I have worked on your SQL Fiddle Sample and removed the obsolete column closing_day.
SET #MyTime = Now();
SELECT
ho.`establishment_id`, e.`name`
FROM
`hours_of_operation` ho
JOIN
`establishments` e ON ho.`establishment_id` = e.`id`
WHERE
NOT `is_closed`
AND
`opening_day` = DATE_FORMAT( CASE
WHEN TIME( #MyTime ) < `opening_time` THEN
DATE( #MyTime ) - INTERVAL 1 DAY
ELSE
DATE( #MyTime )
END, '%a' )
AND
#MyTime >= CAST( CONCAT( CASE
WHEN TIME(#MyTime) < ho.`opening_time` THEN
DATE( #MyTime ) - INTERVAL 1 DAY
ELSE
DATE( #MyTime )
END, ' ', ho.`opening_time` ) AS DATETIME )
AND
#MyTime < CAST( CASE
WHEN ho.`opening_time` > ho.`closing_time` THEN
CONCAT( CASE
WHEN TIME(#MyTime) < ho.`opening_time` THEN
DATE( #MyTime ) - INTERVAL 1 DAY
ELSE
DATE( #MyTime )
END + INTERVAL 1 DAY, ' ', ho.`closing_time` )
ELSE
CONCAT( CASE
WHEN TIME(#MyTime) < ho.`opening_time` THEN
DATE( #MyTime ) - INTERVAL 1 DAY
ELSE
DATE( #MyTime )
END, ' ', ho.`closing_time` )
END AS DATETIME );
The solution is based on the same approach as posted above, but checks the opening hours per weekday.
SQL Fiddle DEMO
I need to select data from MySQL database between the 1st day of the current month and current day.
select*from table_name
where date between "1st day of current month" and "current day"
Can someone provide working example of this query?
select * from table_name
where (date between DATE_ADD(LAST_DAY(DATE_SUB(CURDATE(), interval 30 day), interval 1 day) AND CURDATE() )
Or better :
select * from table_name
where (date between DATE_FORMAT(NOW() ,'%Y-%m-01') AND NOW() )
I was looking for a similar query where I needed to use the first day of a month in my query.
The last_day function didn't work for me but DAYOFMONTH came in handy.
So if anyone is looking for the same issue, the following code returns the date for first day of the current month.
SELECT DATE_SUB(CURRENT_DATE, INTERVAL DAYOFMONTH(CURRENT_DATE)-1 DAY);
Comparing a date column with the first day of the month :
select * from table_name where date between
DATE_SUB(CURRENT_DATE, INTERVAL DAYOFMONTH(CURRENT_DATE)-1 DAY) and CURRENT_DATE
select * from table_name
where `date` between curdate() - dayofmonth(curdate()) + 1
and curdate()
SQLFiddle example
I have used the following query. It has worked great for me in the past.
select date(now()) - interval day(now()) day + interval 1 day
try this :
SET #StartDate = DATE_SUB(DATE(NOW()),INTERVAL (DAY(NOW())-1) DAY);
SET #EndDate = ADDDATE(CURDATE(),1);
select * from table where (date >= #StartDate and date < #EndDate);
Complete solution for mysql current month and current year, which makes use of indexing properly as well :)
-- Current month
SELECT id, timestampfield
FROM table1
WHERE timestampfield >= DATE_SUB(CURRENT_DATE, INTERVAL DAYOFMONTH(CURRENT_DATE)-1 DAY)
AND timestampfield <= LAST_DAY(CURRENT_DATE);
-- Current year
SELECT id, timestampfield
FROM table1
WHERE timestampfield >= DATE_SUB(CURRENT_DATE, INTERVAL DAYOFYEAR(CURRENT_DATE)-1 DAY)
AND timestampfield <= LAST_DAY(CURRENT_DATE);
select * from table
where date between
(date_add (CURRENT_DATE, INTERVAL(1 - DAYOFMonth(CURRENT_DATE)) day)) and current_date;
select * from <table>
where <dateValue> between last_day(curdate() - interval 1 month + interval 1 day)
and curdate();
I found myself here after needing this same query for some Business Intelligence Queries I'm running on an e-commerce store. I wanted to add my solution as it may be helpful to others.
set #firstOfLastLastMonth = DATE_SUB(LAST_DAY(DATE_ADD(NOW(), INTERVAL -2 MONTH)),INTERVAL DAY(LAST_DAY(DATE_ADD(NOW(), INTERVAL -2 MONTH)))-1 DAY);
set #lastOfLastLastMonth = LAST_DAY(DATE_ADD(NOW(), INTERVAL -2 MONTH));
set #firstOfLastMonth = DATE_SUB(LAST_DAY(DATE_ADD(NOW(), INTERVAL -1 MONTH)),INTERVAL DAY(LAST_DAY(DATE_ADD(NOW(), INTERVAL -1 MONTH)))-1 DAY);
set #lastOfLastMonth = LAST_DAY(DATE_ADD(NOW(), INTERVAL -1 MONTH));
set #firstOfMonth = DATE_ADD(#lastOfLastMonth, INTERVAL 1 DAY);
set #today = CURRENT_DATE;
Today is 2019-10-08 so the output looks like
#firstOfLastLastMonth = '2019-08-01'
#lastOfLastLastMonth = '2019-08-31'
#firstOfLastMonth = '2019-09-01'
#lastOfLastMonth = '2019-09-30'
#firstOfMonth = '2019-10-01'
#today = '2019-10-08'
A less orthodox approach might be
SELECT * FROM table_name
WHERE LEFT(table_name.date, 7) = LEFT(CURDATE(), 7)
AND table_name.date <= CURDATE();
as a date being between the first of a month and now is equivalent to a date being in this month, and before now. I do feel that this is a bit easier on the eyes than some other approaches, though.
SELECT date_sub(current_date(),interval dayofmonth(current_date())-1 day) as first_day_of_month;
I had some what similar requirement - to find first day of the month but based on year end month selected by user in their profile page.
Problem statement - find all the txns done by the user in his/her financial year. Financial year is determined using year end month value where month can be any valid month - 1 for Jan, 2 for Feb, 3 for Mar,....12 for Dec.
For some clients financial year ends on March and some observe it on December.
Scenarios - (Today is `08 Aug, 2018`)
1. If `financial year` ends on `July` then query should return `01 Aug 2018`.
2. If `financial year` ends on `December` then query should return `01 January 2018`.
3. If `financial year` ends on `March` then query should return `01 April 2018`.
4. If `financial year` ends on `September` then query should return `01 October 2017`.
And, finally below is the query. -
select #date := (case when ? >= month(now())
then date_format((subdate(subdate(now(), interval (12 - ? + month(now()) - 1) month), interval day(now()) - 2 day)) ,'%Y-%m-01')
else date_format((subdate(now(), interval month(now()) - ? - 1 month)), '%Y-%m-01') end)
where ? is year end month (values from 1 to 12).
The key here is to get the first day of the month. For that, there are several options. In terms of performance, our tests show that there isn't a significant difference between them - we wrote a whole blog article on the topic. Our findings show that what really matters is whether you need the result to be VARCHAR, DATETIME, or DATE.
The fastest solution to the real problem of getting the first day of the month returns VARCHAR:
SELECT CONCAT(LEFT(CURRENT_DATE, 7), '-01') AS first_day_of_month;
The second fastest solution gives a DATETIME result - this runs about 3x slower than the previous:
SELECT TIMESTAMP(CONCAT(LEFT(CURRENT_DATE, 7), '-01')) AS first_day_of_month;
The slowest solutions return DATE objects. Don't believe me? Run this SQL Fiddle and see for yourself 😊
In your case, since you need to compare the value with other DATE values in your table, it doesn't really matter what methodology you use because MySQL will do the conversion implicitly even if your formula doesn't return a DATE object.
So really, take your pick. Which is most readable for you? I'd pick the first since it's the shortest and arguably the simplest:
SELECT * FROM table_name
WHERE date BETWEEN CONCAT(LEFT(CURRENT_DATE, 7), '-01') AND CURDATE;
SELECT * FROM table_name
WHERE date BETWEEN DATE(CONCAT(LEFT(CURRENT_DATE, 7), '-01')) AND CURDATE;
SELECT * FROM table_name
WHERE date BETWEEN (LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH) AND CURDATE;
SELECT * FROM table_name
WHERE date BETWEEN (DATE(CURRENT_DATE) - INTERVAL (DAYOFMONTH(CURRENT_DATE) - 1) DAY) AND CURDATE;
SELECT * FROM table_name
WHERE date BETWEEN (DATE(CURRENT_DATE) - INTERVAL (DAYOFMONTH(CURRENT_DATE)) DAY + INTERVAL 1 DAY) AND CURDATE;
SELECT * FROM table_name
WHERE date BETWEEN DATE_FORMAT(CURRENT_DATE,'%Y-%m-01') AND CURDATE;
I used this one
select DATE_ADD(DATE_SUB(LAST_DAY(now()), INTERVAL 1 MONTH),INTERVAL 1 day) first_day
,LAST_DAY(now()) last_day, date(now()) today_day
All the responses here have been way too complex. You know that the first of the current month is the current date but with 01 as the date. You can just use YEAR() and MONTH() to build the month date by inputting the NOW() method.
Here's the solution:
select * from table_name
where date between CONCAT_WS('-', YEAR( NOW() ), MONTH( NOW() ), '01') and DATE( NOW() )
CONCAT_WS() joins a series of strings with a separator (a dash in this case).
So if today is 2020-08-28, YEAR( NOW() ) = '2020' and MONTH( NOW() ) = '08' and then you just need to append '01' at the end.
Voila!
Get first date and last date from month and year.
select LAST_DAY(CONCAT(year,'.',month,'.','01')) as registerDate from user;
select date_add(date_add(LAST_DAY(end_date),interval 1 DAY),interval -1 MONTH) AS closingDate from user;
SET #date:='2012-07-11';
SELECT date_add(date_add(LAST_DAY(#date),interval 1 DAY),
interval -1 MONTH) AS first_day
i have a table stores activity
a want to generate a overview selecting sum(total) based on the date (unixtime)
like Select SUM(total) from list where date between 123456789 and 2000000
somethings like this ..
but i would like to get more date ranges out with one query
to have a listable result like
sum(total) of 2 days ago
sum(total) of yesterday
sum(total) of today
and so on ...
You don't need a UNION. Something like this should work:
select SUM(total) as Total,
SUM(
case when date >= DATE_SUB(CONCAT(CURDATE(), ' 00:00:00'), INTERVAL 2 DAY)
and date < DATE_SUB(CONCAT(CURDATE(), ' 00:00:00'), INTERVAL 1 DAY) then total end
) TwoDaysAgoTotal,
SUM(
case when date >= DATE_SUB(CONCAT(CURDATE(), ' 00:00:00'), INTERVAL 1 DAY)
and date < CONCAT(CURDATE(), ' 00:00:00') then total end
) as OneDayAgoTotal,
SUM(
case when date >= CONCAT(CURDATE(), ' 00:00:00') then total end
) as TodayTotal
from list
where date between 123456789 and 2000000
Union statement don't solve your case? http://dev.mysql.com/doc/refman/4.1/pt/union.html