Im working on a database that store dates in a varchar(10) mysql field (so sad).
I can not alter the database structure (im building a small plug-in), but i have to query the database finding rows where this data field is between the next 10 days.
Example:
| fid | fdate |
| 1 | 10/09/2010 |
| 2 | 17/09/2010 |
| 3 | 19/09/2010 |
I have to get the rows with fid 1 and 2, becose the date is <= now + 10 days.
Usually i make this kind of query:
SELECT fid FROM table WHERE fdate <= DATE_ADD(NOW(), INTERVAL 10 DAY);
Or, if the date is in the format `yyyymmdd':
SELECT fid FROM table WHERE fdate <= DATE_FORMAT(NOW(), '%Y%m%d');
But theyre useless with the format dd/mm/yyyy.
I've figured out with
SELECT fid, CONCAT(SUBSTRING(fdate, 7, 4), SUBSTRING(fdate, 4, 2), SUBSTRING(fdate, 1, 2)) AS mydate FROM table HAVING mydate <= DATE_ADD(NOW(), INTERVAL 10 DAY);
but i guess it is a bit an overkill rebuilding the date format with concat and substring, and the having clause wont help the query speed.
Any idea?
EDIT
After Adriano's comment, the right solution is
SELECT fid FROM test WHERE STR_TO_DATE(fdate, '%d/%m/%Y') <= DATE_ADD(NOW(), INTERVAL 10 DAY);
so i can avoid the concat and having clause.
What about using str_to_date() to create a date from your format?
EDIT after reading your comment, I created a table like yours:
mysql> SELECT fid, fdate FROM test;
+------+------------+
| fid | fdate |
+------+------------+
| 1 | 10/9/2010 |
| 2 | 17/9/2010 |
| 3 | 19/09/2010 |
+------+------------+
and then did
mysql> SELECT fid FROM test WHERE STR_TO_DATE(fdate, '%d/%m/%Y') <= DATE_ADD(NOW(), INTERVAL 10 DAY);
+------+
| fid |
+------+
| 1 |
| 2 |
+------+
Seems to work. What exactly do you get?
Related
My table contains remind_me_befor_days column and expiry_date column.
I have to select records with condition (expiry_date - remind_me_befor_days) = current_date.
How this possible by query. Anyone can help me ?
Thanks in advance
You can do date arithemtics:
select t.*
from mytable t
where t.expiry_date - interval t.remind_me_befor_days day = current_date
Demo on DB Fiddle:
-- sample data
select * from mytable;
id | expiry_date | remind_me_befor_days
-: | :---------- | -------------------:
1 | 2019-12-25 | 10
2 | 2019-12-25 | 5
-- query
select t.*, current_date
from mytable t
where t.expiry_date - interval t.remind_me_befor_days day = current_date
id | expiry_date | remind_me_befor_days | current_date
-: | :---------- | -------------------: | :-----------
1 | 2019-12-25 | 10 | 2019-12-15
To split this into a few subproblems, we have:
Find the current date.
Subtract your remind_me_befor_days column value from the expiry_date column.
Use that as a filter for the records you want from the table.
The first can be done using the MySQL CURDATE() function. Read more here. Combining that with the second and third steps, you get something that looks like
SELECT *
FROM my_table
WHERE expiry_date - remind_me_befor_days = CURDATE()
I would strongly recommend that you write the condition as:
where expiry_date = curdate() + interval remind_me_before_days day
This is index friendly, so it can make use of an index on expiry_date.
I have this Table in mysql. I want to select data as "rc_type" between "start_date" & "end_date".
+----+--------+------------+------------+
| id | rc_type| start_date | end_date |
+----+--------+------------+------------+
| 1 | Gold | 16-05-2016 | 22-05-2016 |
| 2 | Silver | 20-05-2016 | 29-05-2016 |
+----+--------+------------+------------+
My html form has these three fields. So how to find rc_type?
I've tried this query:
SELECT * FROM room_inventory WHERE rc_type='silver' BETWEEN '16-05-2016' AND '17-05-2016'
And also:
SELECT * FROM room_inventory WHERE start_date >= '16-05-2016' AND end_date <= '17-05-2016' AND rc_type='Gold'
If I select start date as 16-05-2016 and end date as 17-05-2016 with "silver" category selection, I got result with "Gold" category. Actually there is no silver type between this two dates.
I don't get proper data. Kindly Help to get proper Data.
You need to separate presentation from real data type, i.e. is better to convert your date columns to real date format instead of varchar. However in order to get proper result you need to convert there strings to proper dates:
SELECT
*
FROM
room_inventory
WHERE
rc_type = 'Silver'
AND STR_TO_DATE(start_date, '%d-%m-%Y') > STR_TO_DATE('15-05-2016', '%d-%m-%Y')
AND STR_TO_DATE(end_date, '%d-%m-%Y') < STR_TO_DATE('30-05-2016', '%d-%m-%Y');
Output:
+----+---------+------------+------------+
| id | rc_type | start_date | end_date |
+----+---------+------------+------------+
| 2 | Silver | 20-05-2016 | 29-05-2016 |
+----+---------+------------+------------+
1 row in set
If you don't do such converts, you will get incorrect results as follow (I have changed Silver's end date to 29-06-2016):
Input:
mysql> select * from room_inventory;
+----+---------+------------+------------+
| id | rc_type | start_date | end_date |
+----+---------+------------+------------+
| 1 | Gold | 16-05-2016 | 22-05-2016 |
| 2 | Silver | 20-05-2016 | 29-06-2016 |
+----+---------+------------+------------+
2 rows in set
Query:
SELECT
*
FROM
room_inventory
WHERE
rc_type = 'Silver'
AND start_date > '15-05-2016'
AND end_date < '30-05-2016';
Output (wrong):
+----+---------+------------+------------+
| id | rc_type | start_date | end_date |
+----+---------+------------+------------+
| 2 | Silver | 20-05-2016 | 29-06-2016 |
+----+---------+------------+------------+
1 row in set
This is because MySQL performs char-by-char comparison and decides that 29-06-2016 < 30-05-2016 which is wrong. So in general you will have further problems if you keep dates columns in varchar format.
you are using wrong date format,
correct is year-month-day
also you had missing the date column in your query, in this example the query search silver category where start date between 2016-06-16 and 2016-05-17
SELECT * FROM room_inventory WHERE rc_type='silver' BETWEEN '2016-05-16' AND '2016-05-17'
#Danilo mentioned you are using wrong date format,
Another thing is your query formate of comparing date syntax is wrong.
correct syntax:
SELECT * FROM room_inventory WHERE rc_type='silver' AND Your Date BETWEEN '2016-05-16' AND '2016-05-17'.
But here you are comparing two dates so this query won't work as your expectation.
With second query please correct your date format as:
'SELECT * FROM room_inventory WHERE start_date >= '2016-05-16' AND end_date <= '2016-05-17' AND rc_type = 'Gold''
There is no need of 'between' try this:
SELECT * FROM room_inventory WHERE rc_type='silver' AND start_date = '16-05-2016' AND end_date = '17-05-2016'
Logical error , You are use between for two columns in a table. between is used to compare in between one columns ,so you can use AND instead of between .
SELECT * FROM room_inventory WHERE rc_type='silver' AND start_date = '16-05-2016' AND end_date = '17-05-2016'
I have a SQL column like this:
| id | name | date | hour |
I want to select all names between specified start_date, start_hour and end_date, end_hour.
For data like this:
| 1 | one | 2014-12-29 | 11:00 |
| 2 | two | 2014-12-30 | 09:00 |
| 3 | three | 2014-12-30 | 11:00 |
Values:
start_date = 2014-12-29
start_hour = 11:00
end_date = 2014-12-30
end_hour = 10:00`
It should return: one, two.
This looks like a basic sql query. try the following:
select a.name
from (select * from data_table
where date >= start_date
and date <= end_date) as a
where a.hour >= start_hour
and a.hour <= end_hour
the basic idea is to first form a data set of all valid days for all times and then from there pull out the valid times. The other possibly more direct method would be to create a datetime field and then pull on that key.
Let me know if this works for you.
SELECT *
FROM table
WHERE date > '2014-12-28'
AND hour > '10:59'
AND date < '2014-12-31'
AND hour < '10:01'
I'm in trouble with a mysql statement counting appointments for one day within a given time period. I've got a calendar table including starting and finishing column (type = DateTime). The following statement should count all appointments for November including overall appointments:
SELECT
COUNT('APPOINTMENTS') AS Count,
DATE(c.StartingDate) AS Datum
FROM t_calendar c
WHERE
c.GUID = 'blalblabla' AND
((DATE(c.StartingDate) <= DATE('2012-11-01 00:00:00')) AND (DATE(c.EndingDate) >= DATE('2012-11-30 23:59:59'))) OR
((DATE(c.StartingDate) >= DATE('2012-11-01 00:00:00')) AND (DATE(c.EndingDate) <= DATE('2012-11-30 23:59:59')))
GROUP BY DATE(c.StartingDate)
HAVING Count > 1
But how to include appointments that starts before a StartingDate and ends on the StartingDate?
e.g.
StartingDate = 2012-11-14 17:00:00, EndingDate = 2012-11-15 08:00:00
StartingDate = 2012-11-15 09:00:00, EndingDate = 2012-11-15 10:00:00
StartingDate = 2012-11-15 11:00:00, EndingDate = 2012-11-15 12:00:00
My statement returns a count of 2 for 15th of November. But that's wrong because the first appointment is missing. How to include these appointments? What I am missing, UNION SELECT, JOIN, sub selection?
A possible solution?
SELECT
c1.GUID, COUNT('APPOINTMENTS') + COUNT(DISTINCT c2.ANYFIELD) AS Count,
DATE(c1.StartingDate) AS Datum,
COUNT(DISTINCT c2.ANYFIELD)
FROM
t_calendar c1
LEFT JOIN
t_calendar c2
ON
c2.ResourceGUID = c1.ResourceGUID AND
(DATE(c2.EndingDate) = DATE(c1.StartingDate)) AND
(DATE(c2.StartingDate) < DATE(c1.StartingDate))
WHERE
((DATE(c1.StartingDate) <= DATE('2012-11-01 00:00:00')) AND (DATE(c1.EndingDate) >= DATE('2012-11-30 23:59:59'))) OR
((DATE(c1.StartingDate) >= DATE('2012-11-01 00:00:00')) AND (DATE(c1.EndingDate) <= DATE('2012-11-30 23:59:59')))
GROUP BY
c1.ResourceGUID,
DATE(c1.StartingDate)
First: Consolidate range checking
First of all your two range where conditions can be replaced by a single one. And it also seems that you're only counting appointments that either completely overlap target date range or are completely contained within. Partially overlapping ones aren't included. Hence your question about appointments that end right on the range starting date.
To make where clause easily understandable I'll simplify it by using:
two variables to define target range:
rangeStart (in your case 1st Nov 2012)
rangeEnd (I'll rather assume to 1st Dec 2012 00:00:00.00000)
won't be converting datetime to dates only (using date function) the way that you did, but you can easily do that.
With these in mind your where clause can be greatly simplified and covers all appointments for given range:
...
where (c.StartingDate < rangeEnd) and (c.EndingDate >= rangeStart)
...
This will search for all appointments that fall in target range and will cover all these appointment cases:
start end
target range |==============|
partial front |---------|
partial back |---------|
total overlap |---------------------|
total containment |-----|
Partial front/back may also barely touch your target range (what you've been after).
Second: Resolving the problem
Why you're missing the first record? Simply because of your having clause that only collects those groups that have more than 1 appointment starting on a given day: 15th Nov has two, but 14th has only one and is therefore excluded because Count = 1 and is not > 1.
To answer your second question what am I missing is: you're not missing anything, actually you have too much in your statement and needs to simplified.
Try this statement instead that should return exactly what you're after:
select count(c.GUID) as Count,
date(c.StartingDate) as Datum
from t_calendar c
where (c.GUID = 'blabla') and
(c.StartingDate < str_to_date('2012-12-01', '%Y-%m-%d') and
(c.EndingDate >= str_to_date('2012-11-01', '%Y-%m-%d'))
group by date(c.StartingDate)
I used str_to_date function to make string to date conversion more safe.
I'm not really sure why you included having in your statement, because it's not really needed. Unless your actual statement is more complex and you only included part that's most relevant. In that case you'll likely have to change it to:
having Count > 0
Getting appointment count per day in any given date range
There are likely other ways as well but the most common way would be using a numbers or ?calendar* table that gives you the ability to break a range into individual points - days. They you have to join your appointments to this numbers table and provide results.
I've created a SQLFiddle that does the trick. Here's what it does...
Suppose you have numbers table Num with numbers from 0 to x. And appointments table Cal with your records. Following script created these two tables and populates some data. Numbers are only up to 100 which is enough for 3 months worth of data.
-- appointments
create table Cal (
Id int not null auto_increment primary key,
StartDate datetime not null,
EndDate datetime not null
);
-- create appointments
insert Cal (StartDate, EndDate)
values
('2012-10-15 08:00:00', '2012-10-20 16:00:00'),
('2012-10-25 08:00:00', '2012-11-01 03:00:00'),
('2012-11-01 12:00:00', '2012-11-01 15:00:00'),
('2012-11-15 10:00:00', '2012-11-16 10:00:00'),
('2012-11-20 08:00:00', '2012-11-30 08:00:00'),
('2012-11-30 22:00:00', '2012-12-05 00:00:00'),
('2012-12-01 05:00:00', '2012-12-10 12:00:00');
-- numbers table
create table Nums (
Id int not null primary key
);
-- add 100 numbers
insert into Nums
select a.a + (10 * b.a)
from (select 0 as a union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9) as a,
(select 0 as a union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9) as b
Now what you have to do now is
Select a range of days which you do by selecting numbers from Num table and convert them to dates.
Then join your appointments to those dates so that those appointments that fall on particular day are joined to that particular day
Then just group all these appointments per each day and get results
Here's the code that does this:
-- just in case so comparisons don't trip over
set names 'latin1' collate latin1_general_ci;
-- start and end target date range
set #s := str_to_date('2012-11-01', '%Y-%m-%d');
set #e := str_to_date('2012-12-01', '%Y-%m-%d');
-- get appointment count per day within target range of days
select adddate(#s, n.Id) as Day, count(c.Id) as Appointments
from Nums n
left join Cal c
on ((date(c.StartDate) <= adddate(#s, n.Id)) and (date(c.EndDate) >= adddate(#s, n.Id)))
where adddate(#s, n.Id) < #e
group by Day;
And this is the result of this rather simple select statement:
| DAY | APPOINTMENTS |
-----------------------------
| 2012-11-01 | 2 |
| 2012-11-02 | 0 |
| 2012-11-03 | 0 |
| 2012-11-04 | 0 |
| 2012-11-05 | 0 |
| 2012-11-06 | 0 |
| 2012-11-07 | 0 |
| 2012-11-08 | 0 |
| 2012-11-09 | 0 |
| 2012-11-10 | 0 |
| 2012-11-11 | 0 |
| 2012-11-12 | 0 |
| 2012-11-13 | 0 |
| 2012-11-14 | 0 |
| 2012-11-15 | 1 |
| 2012-11-16 | 1 |
| 2012-11-17 | 0 |
| 2012-11-18 | 0 |
| 2012-11-19 | 0 |
| 2012-11-20 | 1 |
| 2012-11-21 | 1 |
| 2012-11-22 | 1 |
| 2012-11-23 | 1 |
| 2012-11-24 | 1 |
| 2012-11-25 | 1 |
| 2012-11-26 | 1 |
| 2012-11-27 | 1 |
| 2012-11-28 | 1 |
| 2012-11-29 | 1 |
| 2012-11-30 | 2 |
If I have a MySQL query to aggregate totals by week, e.g.:
select sum(keyword1), sum(keyword2), sum(keyword3), dateTime
from myTable
group by week(dateTime)
order by dateTime asc
I find that the weeks appear to begin on a Sunday.
Can this be changed to a Monday?
Column dateTime is in MySQL Timestamp format 2011-09-26 12:34:32.
You want the sunday to belong to the previous week so remove one day from it
select sum(keyword1), sum(keyword2), sum(keyword3), week(DATE_SUB(dateTime, INTERVAL 1 DAY)) my_week
from myTable
group by week(DATE_SUB(dateTime, INTERVAL 1 DAY))
order by my_week asc
mysql> select week('2011-09-25', 1);
+-----------------------+
| week('2011-09-25', 1) |
+-----------------------+
| 38 |
+-----------------------+
mysql> select week('2011-09-25', 0);
+-----------------------+
| week('2011-09-25', 0) |
+-----------------------+
| 39 |
+-----------------------+
So, http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_default_week_format