Trying to solve date conditional in MySQL select query - mysql

I am trying to list upcoming deliveries that can still be chosen for shipping. Each of these deliveries requires a certain amount of lead-time to ship - so it's not realistic to expect your delivery to come tomorrow if you order at 5pm the day before.
The deliveries table defines a "leadTime" which is an integer of days and also a "nextDate" which contains a date field in "YYYY-MM-DD" format. I need to filter out the rows where the "nextDate" is at least one day away - or rather, one day away if before 10am and two days away if after 10am.
SELECT d.* FROM delivery d
WHERE d.nextDate >= DATE_ADD( CURRENT_DATE, INTERVAL d.leadTime DAY )
ORDER BY d.nextDate ASC, d.id ASC
For example, (assuming a leadTime of one day). If it was 9am Wednesday - I can see all records from Thursday on. However, if it's 11am Wednesday I can only see from Friday on (since it's after 10am there isn't enough time to prepare).

Use DATE(NOW() + INTERVAL 14 HOUR) in place of CURRENT_DATE or, combining the whole thing together:
SELECT *
FROM delivery
WHERE nextDate >= DATE(NOW() + INTERVAL 14 HOUR + INTERVAL leadTime DAY)
ORDER BY nextDate ASC, id ASC

Related

How to show current months projects in list, where current month date is between A and B on new year roll over

Last year I set up a project management system, but now as we're on a new year I've been bit in my ass by some newbie coding (of me) :)
So now I really need your help to understand how to fix this in a good way once and for all.
The error:
The major problem right now is in the year roll over, right now my SQL query dosn't understand that the month of the new year (1) is larger than the current years (12) which then dosn't show the right projects in the list.
Any ideas? Thanks in advance!
This is my current SQL Query:
SELECT *
FROM projects
WHERE MONTH(CURDATE()) between MONTH(project_start) and MONTH(project_delivery)
AND YEAR(CURDATE()) between YEAR(project_start) and YEAR(project_delivery)
order by project_id
This is the raw structure of the table projects:
Project_id projet_start projet_delivery
1 2018-12-20 2018-12-22
2 2018-12-25 2018-12-29
3 2018-12-28 2018-12-28
4 2018-12-30 2019-01-22
BETWEEN works for actual dates:
SELECT *
FROM projects
WHERE CURDATE() between project_start and project_delivery
order by project_id
If you wanted everything from the month (month granularity) rather than day granularity:
SELECT *
FROM projects
WHERE CURDATE() between DATE_SUB(project_start, INTERVAL DAY(project_start)-1 DAY) and
DATE_ADD(DATE_SUB(project_delivery, INTERVAL DAY(project_delivery) DAY), INTERVAL 1 MONTH)
order by project_id
So if a project started on Dec 5th and finished on Jan 19th, this would give everything between Dec 1st and Jan 31st. Do bear in mind though that if any date on Jan 31st also has a time component (i.e. later than midnight), it will mean it's fractionally after this end date and won't show.
Comment if that's the case and you want help solving it (easiest to not use BETWEEN because it's always inclusive at each end, use < which is exclusive)
Update: Projects that have had some activity this month, i.e a project that:
started before, ended during
started before, ended after
started during, ended during
started during, ended after
The common thing all these have is that the start date of the project is before the end of this month, and also the end date of the project is after the first of this month
SELECT *
FROM projects
WHERE
--started before the end of this month
project_start < DATE_ADD(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY), INTERVAL 1 MONTH) AND
--ended after the start of this month
project_delivery > DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) DAY)
order by project_id
Doing a DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) DAY) is a rather convoluted way of writing "subtract the current day number from the current date" i.e. 2019-01-02 minus 2 -> 2018-12-31. We look for dates > this (so as not to include it)
Similarly, DATE_ADD(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY), INTERVAL 1 MONTH) Takes the current date, subs DAY-1 (so just 1 this time, not 2) off it to reach 1st of this month, then adds a month on to get to first of next month. 2019-01-02 -> 2019-01-01 -> 2019-02-01. Again < it so it's exclusive
There's a bug in going the other way (add a month then sub the day) if you're on eg the 31st of jan and you add a month first - there is no 31st of feb so mysql will cap at 28th of feb, then sub 31 days off it, giving a date that is not the end of jan (i.e. 28th of jan)
Just use date comparisons:
select p.*
from projects p
where curdate() >= project_start and
curdate() <= project_delivery;
I'm not sure why you would want to break the dates into their time components. However, that is totally unnecessary to compare them.
If you want to just do the comparison at the month level, then one method is to convert the values to months:
select p.*
from projects p
where year(curdate()) * 12 + month(curdate()) >= year(project_start) * 12 + month(project_start) and
year(curdate()) * 12 + month(curdate()) <= year(project_delivery) * 12 + month(project_delivery);
Alternatively, just move the dates to the beginning of months:
select p.*
from projects p
where curdate() >= project_start + interval 1 - day(project_start) day and
curdate() < ( project_delivery + interval (1 - day(project_delivery) day) + interval 1 month;

mysql query how to show each day's total cash sale for the current week

I have the following mysql query which shows the each day's total cash sale for the current week.
SELECT
sum(Price) as totalprice,
WEEKDAY(CreatedOn) as dayno,
DATE(CreatedOn) as CreatedOn,
AgentID
FROM records
WHERE CreatedOn BETWEEN (CURDATE()-WEEKDAY(CURDATE())) AND CURDATE()
GROUP BY DATE(CreatedOn)
When I run the query it looks like this:
There are records on 30th November(today's date). So,
day 0 (Monday) no cash sale
day 1 (Tuesday) $5049
day 2 (Wednsday) $99
Nothing is displayed for day 3 (Thursday/today). I cannot figure out the reason there are definitely record in the database but cannot get them to be displayed. I would appreciate any help.
CURDATE() is today's date but at 00:00:00+0000000
"push up" the higher date by 1 day and avoid using between for date/time ranges:
WHERE CreatedOn >= date_sub(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)
AND CreatedOn < date_add(CURDATE(), INTERVAL 1 DAY)
select date_sub(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)
, date_add(CURDATE(), INTERVAL 1 DAY)
The condition in the query currently specifies on or before midnight today, so any rows for today after midnight are going to be excluded.
I think you are intending to specify CreatedOn before midnight of the following day.
I also suggest you don't subtract an integer value from a date/datetime, and instead use the INTERVAL syntax
WHERE CreatedOn >= CURDATE() - INTERVAL WEEKDAY(CURDATE()) DAY
AND CreatedOn < CURDATE() + INTERVAL 1 DAY
To test those expressions before we include them in a query, we can run a SELECT statement:
SELECT CURDATE() - INTERVAL WEEKDAY(CURDATE()) DAY
, CURDATE() + INTERVAL 1 DAY
and verify that those expressions are returning what we expect, the values we want to use. For testing, we can replace CURDATE() with a date value to test the return for days other than today.

mysql optimize query where date hour

Hi all, I have pretty awfull query, that needs optimizing.
I need to select all records where date of created matches NOW - 35days, but the minutes and seconds can be any.
So I have this query here, its ugly, but working:
Any optimisation tips are welcome!
SELECT * FROM outbound_email
oe
INNER JOIN (SELECT `issue_id` FROM `issues` WHERE 1 ORDER BY year DESC, NUM DESC LIMIT 0,5) as issues
ON oe.issue_id = issues.issue_id
WHERE
year(created) = year( DATE_SUB(NOW(), INTERVAL 35 DAY) ) AND
month(created) = month( DATE_SUB(NOW(), INTERVAL 35 DAY) ) AND
day(created) = day( DATE_SUB(NOW(), INTERVAL 35 DAY) ) AND
hour(created) = hour( DATE_SUB(NOW(), INTERVAL 35 DAY) )
AND campaign_id IN (SELECT id FROM campaigns WHERE initial = 1)
I assume the field "created" is a datetime field and is from the issues table? Since you don't need anything else on the issues and campaign table, then you can do the following:
SELECT e.* FROM outbound_email e
JOIN issues i ON e.issue_id = i.issue_id
JOIN campaigns c ON c.id = i.campaign_id
WHERE i.created < DATE_SUB(NOW(), INTERVAL 35 DAY)
AND c.initial = 1
There's no need to separate the datetime field into years, months...etc.
You seem to be saying you want to select all rows from a table where the time they were created was the same hour as it is currently, 35 days ago
SELECT * FROM table WHERE created BETWEEN
DATE_ADD(CURDATE(), INTERVAL (HOUR(now()) - 840) HOUR) AND
DATE_ADD(CURDATE(), INTERVAL (HOUR(now()) - 839) HOUR)
Why does it work? Curdate gives us today at midnight. We add to this the current hour of the time (e.g. Suppose it's now 5pm we'd add `HOUR(NOW()) which would give us 17, for a time now of 5pm) but we also subtract 840 because that's 35 days * 24 hours a day = 840 hours. Date add will hence add -823 hours to the current date, i.e. 5pm 35 days ago
We make the search a range to get all the records from the hour, the simplest way to specify an hour later is to subtract 839 hours instead of 840
Technically this query will also return records that are bang on 6pm (but not a second later) 35 days ago too because between is inclusive (between 1 and 10 will return 10 also
If this is a problem, change the BETWEEN for created >= blah AND created < blahblah
I haven't put the rest of your query in for reasons of clarity
As a side note, the way you did it wasn't bad- you could have simplified things by not having the year/month/day parts, just dropping the time part of the date with date(created) = date_sub(curdate(), interval 35 day) which is the year month and day combined as a date, no time element.. BUT it is generally always best to leave table data alone rather than format or convert it just to match a query. If you convert table data then indexes can no longer be used. If you go the extra mile to get your query parameters into the format of the column, and don't convert the table data then indexes on the column can be used

sql query to show records created today, last week and last month

I want to write some sql queries to show me records from mysql database that are created today, records created last week, and records created last month..The first query show me results created in last 7 days (including today). For example if today is Sunday, I want to see results created from Monday to Saturday (not today). Similarly, in the second query I want to see records created last month (excluding records of this month). For example, if this is June, I want to see records created in May
SELECT COUNT(*) AS stdtotal FROM `login`
WHERE `account_created_date` > DATE_SUB(NOW(), INTERVAL 7 DAY)
and the other query is
SELECT COUNT(*) AS stdtotal FROM `login`
WHERE `account_created_date` > DATE_SUB(NOW(), INTERVAL 30 DAY)
Note that my table name is login and the column name is account_created_date of type date.
Just fix the where clauses. For the previous 7 days:
WHERE account_created_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND
account_created_date < CURDATE()
For the previous month:
WHERE account_created_date >= DATE_SUB(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE) - 1 DAY), INTERVAL 1 MONTH) AND
account_created_date < DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE) - 1 DAY)
This calculates the first day of the current month and then goes one month prior to that.

Upcoming birthdays year bug (SQL)

this is my current sql query that gets all the upcoming birthdays for my company in the next 90 days:
SELECT
user.birthday, user.name, MONTH(user.birthday)
AS month, DAY(user.birthday) AS day
FROM user WHERE
(1 =
(FLOOR(DATEDIFF(DATE_ADD(DATE(NOW()),INTERVAL
90 DAY),birthday) / 365.25)) -
(FLOOR(DATEDIFF(DATE(NOW()),birthday)
/ 365.25)))
ORDER BY MONTH(birthday),DAY(birthday)
The problem, is that if right now is november, and there are some birthdays in january, it will display january birthdays first, then november and then december, although january birthdays already happened THIS year.
Is there a way to reorder this records in the same SQL query, so that it displays current and future months first, and THEN next year's months?
First partial solution thanks to Johan
ORDER BY ( MONTH(birthday) > MONTH(NOW()
OR ((MONTH(birthday) = MONTH(now())
AND DAY(birthday) >= DAY(NOW()) DESC
, MONTH(birthday), DAY(birthday)
Still it needs a little improvement. If a birthday already happened, it should be displayed AFTER december on the results. Example of what should be displayed assuming it is 27th june
28 june: john doe
27 december: mary wright
5 june (next year of course): mad max
I'm not sure but it seems that your birthday includes the year. If that's so than you'll have a range of birthdays per user (one for every year) and you can just select the ones within the next 90 days.
SELECT
user.birthday
, user.name
, MONTH(user.birthday) AS month
, DAY(user.birthday) AS day
FROM user
WHERE birthday BETWEEN NOW() AND DATE_ADD(NOW, INTERVAL 90 DAY)
ORDER BY Birthday DESC
If your birthday only has a month and day, your query needs to be:
SELECT
user.birthday
, user.name
, MONTH(user.birthday) AS month
, DAY(user.birthday) AS day
FROM user
WHERE STR_TO_DATE(CONCAT(YEAR(NOW()),MONTH(birthday),DAY(birthday)),'%YYYY%M%D')
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY) OR
STR_TO_DATE(CONCAT(YEAR(DATE_ADD(NOW(),INTERVAL 1 YEAR)),MONTH(birthday),DAY(birthday)),'%YYYY%M%D')
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY)
ORDER BY ( MONTH(birthday) > MONTH(NOW()
OR ((MONTH(birthday) = MONTH(now()) AND DAY(birthday) >= DAY(NOW()) DESC,
MONTH(birthday), DAY(birthday)
I believe you need to order using something that includes the year.
ORDER by date_format( date, "%d/%m/%Y" )
I am no expert but something like this may work too.
ORDER BY YEAR(birthday),MONTH(birthday),DAY(birthday)
I think you want to know if each user's birthday, brought in to the current year or the next year, falls between your range:
SELECT name, birthday
FROM (SELECT name, birthday, YEAR(NOW()) - YEAR(birthday) AS years_ago
FROM user) d
WHERE DATE_ADD(birthday, INTERVAL years_ago YEAR)
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY)
OR
DATE_ADD(birthday, INTERVAL (years_ago + 1) YEAR)
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY);
(It occurs to me that you might actually want INTERVAL 3 MONTH, rather than 90 DAY, expecially if you intend to run this query on the first of every month.)
Your query will create a full table scan.
Store an integer containing the day of the year (1st of april is going to be around 90), and compare that with the current day of the year.
I've been searching for this code, but I couldn't find a clean/simple query (that also works with leap-years (29th of february problem))
So i've made my own.
Here's the simplest code to get the upcoming birthdays for the next x days, (this query also displays the birthdays of yesterday (or you can change it to a x number of days in the past)
SELECT name, date_of_birty
FROM users
WHERE DATE(CONCAT(YEAR(CURDATE()), RIGHT(date_of_birty, 6)))
BETWEEN
DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND
DATE_ADD(CURDATE(), INTERVAL 5 DAY)