<= displays records of < in mysql query - mysql

In MySQL, I am using the following query to get records having the UpdatedAt field in between 2015-02-02 and 2015-02-06
SELECT `TotalPrice`,`UpdatedAt` FROM `price` WHERE `UpdatedAt`>= '2015-02-02' AND `UpdatedAt`<='2015-02-06' ORDER BY `UpdatedAt` DESC
And this is displaying UpdatedAt from 2015-02-05 to 2015-02-02. And the table has records for 2015-02-06 also. Kindly indicate the mistake i have done.

My guess is that UpdatedAt has a time component. Here is an alternative where clause:
WHERE `UpdatedAt` >= '2015-02-02' AND `UpdatedAt` < '2015-02-07'
You might find this more readable as:
WHERE `UpdatedAt` >= '2015-02-02' AND
`UpdatedAt` < DATE_ADD('2015-02-06', INTERVAL 1 DAY)
What you should not get in the habit of doing is:
WHERE DATE(UpdatedAt) >= '2015-02-02' AND DATE(UpdatedAt) <= '2015-02-06'
Although technically correct, this prevents the use of indexes.

Related

Selecting from table where timestamp between X doesn't return all rows

I have database table with different records and they all have timestamp with them.
When I want to get a certain month (for example April) records is use following query:
SELECT *
FROM `water`
WHERE timestamp >= DATE_FORMAT('2020-04-01', '%Y-%m-%d')
AND timestamp <= DATE_FORMAT('2020-04-30', '%Y-%m-%d')
AND watercar='JV03'
ORDER by timestamp DESC
It will return me records which timestamp is between 01.04.2020-29.04.2020 but it misses the last day of april 30.04.2020 record.
I also tried >= <= and between operators, same issue although the record does exist.
What am I missing?
DB Fiddle: https://www.db-fiddle.com/f/nWFFZmUt7FM17c98DXRRQw/0
Update your query to this:
SELECT *
FROM `water`
WHERE timestamp between DATE_FORMAT('2020-04-01', '%Y-%m-%d 00:00:00') AND DATE_FORMAT('2020-04-30', '%Y-%m-%d 23:59:59') AND watercar='JV03'
ORDER by timestamp DESC
or
SELECT *
FROM `water`
WHERE DATE(timestamp) between DATE_FORMAT('2020-04-01', '%Y-%m-%d') AND DATE_FORMAT('2020-04-30', '%Y-%m-%d') AND watercar='JV03'
ORDER by timestamp DESC
First, there should be no need to use date_format). MySQL should understand dates in the YYYY-MM-DD format.
Second, do not use between with date/time values. Instead, to get everything in April, use:
where timestamp >= date('2020-04-01') and
timestamp < date('2020-05-01')
This formulation works both when the column as a time component and when it does not. So, I recommend it in all situation.
If you want to pass in the end date as a parameter, you can use:
where timestamp >= :start_dt and
timestamp < :end_dt + interval 1 day

Sql date comparison in where clause is not working as expected

I'm facing a strange mysql behavior...
If I want to return the rows from "MyTable" with a date lower than date-10 seconds ago or a future date
I also store future date because in my real program, I "launch" some queries with delay and date is actually the last query date...i.e.: a kind of queue...:
SELECT (NOW() - date) AS new_delay, id
FROM MyTable
WHERE (NOW() - date < 10)
ORDER BY new_delay DESC;
This one does not work as expected: It returns all the entries:
EDIT: here is the result:
However, this one is working just fine:
SELECT (NOW() - date) AS new_delay, id
FROM MyTable
WHERE (NOW() < date + 10)
ORDER BY new_delay DESC;
DB example:
CREATE TABLE IF NOT EXISTS `MyTable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
INSERT INTO `MyTable` (`id`, `date`) VALUES
(1, (NOW())),
(2, (NOW()-10)),
(3, (NOW()+100));
Any ideas??
Don't do the comparisons like that. In a numeric context now() end up being converted to an integer -- and in an arcane format. Instead, use DATEDIFF() or just regular comparisons. For instance, if you want the difference in days:
SELECT datediff(curdate(), date) as new_delay, id
FROM MyTable
WHERE date >= date_sub(now(), interval 10 day)
ORDER BY new_delay DESC;
use mysql DATEDIFF
select DATEDIFF(curdate(),date) as new_delay, id from MyTable
where date >= date_sub(curdate(), interval 10 day)
ORDER BY new_delay DESC;
DATEDIFF() function returns the time between two dates
As proposed by #Gordon in the his answer, I can use the date_sub / date_add functions...
I can correct the where clause to be :
WHERE NOW() < date_add(ServerRequests.date, interval 10 second)
OR
WHERE date > date_sub(now(), interval 10 second)
OR as proposed in my initial post:
WHERE (NOW() < date + 10)
But I still don't see why I cannot use the sub operation...So if anyone can give me a reason, I would be happy to understand...

MySQL date interval on select

i've made this SQL code :
CREATE TABLE `logs` (
`id_log` INT(11) NOT NULL AUTO_INCREMENT,
`data_log` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id_log`),
)
i made it to Insert a record when my server goes down,but i would like to make some check if it wasn't Inserted the same record 10 minutes before.
So i was looking for some SELECT that shows only records from NOW() to 10 minutes before.
You're looking for INTERVAL # [UNIT], there's various ways to use it -- http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html:
SELECT count(*)
FROM logs
WHERE data_log > NOW() - INTERVAL 10 MINUTE;
This will return the count of records written to the log in the last ten minutes:
SELECT Count(*) as count_in_last_10 FROM logs WHERE data_log BETWEEN DATE(NOW()-INTERVAL 10 MINUTE) AND NOW()

How to select rows that have current day's timestamp?

I am trying to select only today's records from a database table.
Currently I use
SELECT * FROM `table` WHERE (`timestamp` > DATE_SUB(now(), INTERVAL 1 DAY));
But this takes results for the last 24 hours, and I need it to only select results from today, ignoring the time. How can I select results based on the date only ?
use DATE and CURDATE()
SELECT * FROM `table` WHERE DATE(`timestamp`) = CURDATE()
Warning! This query doesn't use an index efficiently. For the more efficient solution see the answer below
see the execution plan on the DEMO
If you want an index to be used and the query not to do a table scan:
WHERE timestamp >= CURDATE()
AND timestamp < CURDATE() + INTERVAL 1 DAY
To show the difference that this makes on the actual execution plans, we'll test with an SQL-Fiddle (an extremely helpful site):
CREATE TABLE test --- simple table
( id INT NOT NULL AUTO_INCREMENT
,`timestamp` datetime --- index timestamp
, data VARCHAR(100) NOT NULL
DEFAULT 'Sample data'
, PRIMARY KEY (id)
, INDEX t_IX (`timestamp`, id)
) ;
INSERT INTO test
(`timestamp`)
VALUES
('2013-02-08 00:01:12'),
--- --- insert about 7k rows
('2013-02-08 20:01:12') ;
Lets try the 2 versions now.
Version 1 with DATE(timestamp) = ?
EXPLAIN
SELECT * FROM test
WHERE DATE(timestamp) = CURDATE() --- using DATE(timestamp)
ORDER BY timestamp ;
Explain:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF
1 SIMPLE test ALL
ROWS FILTERED EXTRA
6671 100 Using where; Using filesort
It filters all (6671) rows and then does a filesort (that's not a problem as the returned rows are few)
Version 2 with timestamp <= ? AND timestamp < ?
EXPLAIN
SELECT * FROM test
WHERE timestamp >= CURDATE()
AND timestamp < CURDATE() + INTERVAL 1 DAY
ORDER BY timestamp ;
Explain:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF
1 SIMPLE test range t_IX t_IX 9
ROWS FILTERED EXTRA
2 100 Using where
It uses a range scan on the index, and then reads only the corresponding rows from the table.
SELECT * FROM `table` WHERE timestamp >= CURDATE()
it is shorter , there is no need to use 'AND timestamp < CURDATE() + INTERVAL 1 DAY'
because CURDATE() always return current day
MySQL CURDATE() Function
Or you could use the CURRENT_DATE alternative, with the same result:
SELECT * FROM yourtable WHERE created >= CURRENT_DATE
Examples from database.guide
If you want to compare with a particular date , You can directly write it like :
select * from `table_name` where timestamp >= '2018-07-07';
// here the timestamp is the name of the column having type as timestamp
or
For fetching today date , CURDATE() function is available , so :
select * from `table_name` where timestamp >= CURDATE();
This could be the easiest in my opinion:
SELECT * FROM `table` WHERE `timestamp` like concat(CURDATE(),'%');
Simply cast it to a date:
SELECT * FROM `table` WHERE CAST(`timestamp` TO DATE) == CAST(NOW() TO DATE)
How many ways can we skin this cat? Here is yet another variant.
SELECT * FROM table WHERE DATE(FROM_UNIXTIME(timestamp)) = '2015-11-18';
On Visual Studio 2017, using the built-in database for development I had problems with the current given solution, I had to change the code to make it work because it threw the error that DATE() was not a built in function.
Here is my solution:
where CAST(TimeCalled AS DATE) = CAST(GETDATE() AS DATE)
SELECT * FROM table WHERE FROM_UNIXTIME(your_column_with_unix_time,'%Y-%m-%d') = CURDATE()

MySQL query runs very slow on large table

I am trying to run the following query on a very large table with over 90 million of rows increasing
SELECT COUNT(DISTINCT device_uid) AS cnt, DATE_FORMAT(time_start, '%Y-%m-%d') AS period
FROM game_session
WHERE account_id = -2 AND DATE_FORMAT(time_start '%Y-%m-%d') BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
GROUP BY period
ORDER BY period DESC
I have the following table structure:
CREATE TABLE `game_session` (
`session_id` bigint(20) NOT NULL,
`account_id` bigint(20) NOT NULL,
`authentification_type` char(2) NOT NULL,
`source_ip` char(40) NOT NULL,
`device` char(50) DEFAULT NULL COMMENT 'Added 0.9',
`device_uid` char(50) NOT NULL,
`os` char(50) DEFAULT NULL COMMENT 'Added 0.9',
`carrier` char(50) DEFAULT NULL COMMENT 'Added 0.9',
`protocol_version` char(20) DEFAULT NULL COMMENT 'Added 0.9',
`lang_key` char(2) NOT NULL DEFAULT 'en',
`instance_id` char(100) NOT NULL,
`time_start` datetime NOT NULL,
`time_end` datetime DEFAULT NULL,
PRIMARY KEY (`session_id`),
KEY `game_account_session_fk` (`account_id`),
KEY `lang_key_fk` (`lang_key`),
KEY `lookup_active_session_idx` (`account_id`,`time_start`),
KEY `lookup_finished_session_idx` (`account_id`,`time_end`),
KEY `start_time_idx` (`time_start`),
KEY `lookup_guest_session_idx` (`device_uid`,`time_start`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
How can I optimize this?
Thank for your answer
DATE_FORMAT(time_start '%Y-%m-%d') sounds expensive.
Every calculation on a column reduces the use of indexes. You probably run in to a full index scan + calculation of DATE_FORMAT for each value instead of a index lookup / range scan.
Try to store the computed value in the column (or create a computed index if mysql supports it). Or even better rewrite your conditions to compare directly to the value stored in the column.
Well, 90mlns is a lot, but I suspect it doesn't use the start_time_idx because of the manipulations, which you can avoid (you can manipulate the values you compare it with with, it also must be done only once per query if mysql is smart enough), have you checked EXPLAIN?
You may want to group and sort by time_start instead of the period value you create when the query is run. Sorting by period requires all of those values to be generated before any sorting can be done.
Try swapping out your WHERE clause with the following:
WHERE account_id = -2 AND time_start BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
MySQL will still catch the dates between, the only ones you'll need to worry about are the ones from today, which might get truncated due to technically being greater than midnight.
You can fix that by incrementing the second CURDATE( ) with CURDATE( ) + INTERVAL 1 DAY
I'd change
BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
to
> (CURDATE() - INTERVAL 90 DAY)
You don't have records from future, do you?
Change the query to:
SELECT COUNT(DISTINCT device_uid) AS cnt
, DATE_FORMAT(time_start, '%Y-%m-%d') AS period
FROM game_session
WHERE account_id = -2
AND time_start >= CURDATE() - INTERVAL 90 DAY
AND time_start < CURDATE() + INTERVAL 1 DAY
GROUP BY DATE(time_start) DESC
so the index of (account_id, time_start) can be used for the WHERE part of the query.
If it's still slow - the DATE(time_start) does not look very good for performance - add a date_start column and store the date part of time_start.
Then add an index on (account_id, date_start, device_uid) which will further improve performance as all necessary info - for the GROUP BY date_start and the COUNT(DISTINCT device_uid) parts - will be on the index:
SELECT COUNT(DISTINCT device_uid) AS cnt
, date_start AS period
FROM game_session
WHERE account_id = -2
AND date_start BETWEEN CURDATE() - INTERVAL 90 DAY
AND CURDATE()
GROUP BY date_start DESC