why mysql timestamp column use timestamp to search get larger result? - mysql

select count(*) from PN_Review where addTime >= '2017-05-01 00:00:00';
result:3468
select count(*) from PN_Review where addTime >= 1493568000;
result:37645 which is exactly larger than fact.
addTime column is created by timestamp
what I do wrong with second query?

Here's what's happening with that second query.
MySQL to accessing every row in the table, and converting the value in the addTime (TIMESTAMP) column into a string value. And then comparing the string value to a literal `'1493568000'.
addTime >= 1493568000
that's essentially the same as specifying `
addTime >= '1493568000'
which is equivalent to
DATE_FORMAT(addTime,'%Y-%m-%d %T) >= '1493568000'
All of those will evaluate to TRUE for an addTime value of '1970-01-02', and for any non-NULL value other than '0000-00-00'. And MySQL has to perform that conversion and comparison for every row in the table.
Consider adding MIN(addTime) to the SELECT list, along with the COUNT(*), and I expect the value returned will differ from the value returned by the first query.
With the expression in the first query:
addTime >= '2017-05-01 00:00:00'
MySQL evaluates the literal string on the right side as a datetime value in the current timezone, and then compares that to the value of addTime TIMESTAMP column. (With this form, MySQL can make use of an index (with addTime as the leading column) to perform a range scan operation.)
Here are some other forms to consider:
UNIX_TIMESTAMP(addTime) >= 1493568000
The UNIX_TIMESTAMP function will return the internal integer "seconds" value from the TIMESTAMP,and compare that to the numeric literal.
addTime >= FROM_UNIXTIME(1493568000)
The value returned by the FROM_UNIXTIME function is influenced by the setting of the session time_zone variable. The returned value is compared to the TIMESTAMP column.
Consider this demonstration:
SET time_zone = '+05:00' ;
SELECT ##time_zone, FROM_UNIXTIME(1493568000) ;
returns
##time_zone FROM_UNIXTIME(1493568000)
----------- -------------------------
+05:00 2017-04-30 21:00:00
compare to
SET time_zone = '+00:00' ;
SELECT ##time_zone, FROM_UNIXTIME(1493568000) ;
which returns
##time_zone FROM_UNIXTIME(1493568000)
----------- -------------------------
+00:00 2017-04-30 16:00:00

Related

A more elegant way to get current year-month to insert in a 'where' clause

The table I'm querying from has this DateTime column.
created_time
2022-03-19T15:21:52+08:00
2022-03-19T13:10:22+08:00
2022-03-19T13:09:52+08:00
2022-03-19T13:02:47+08:00
2022-03-20T20:51:03+08:00
select extract(year_month from curtime())
Using extract (as above) doesn't work as it will get me: 202203.
SELECT created_time
FROM `freemark-prod-zohocrm`.patients p
where select extract(year_month from curtime())
Therefore the query above will not give me any result as my 'where' clause needs to specifically ask for '2022-03%' and not 202203.
SELECT created_time
FROM `freemark-prod-zohocrm`.patients p
where date_format(p.created_time, '%Y')=(select extract(year from curtime()))
and date_format(p.created_time, '%m')=(select extract(month from curtime()))
Therefore I am currently using the query above to obtain Year='2022' AND Month='03' which I feel doesn't look that nice and might cause me future problems.
I am wondering if there is a more elegant way to get the current 'Year-Month' (eg.'2022-03%') to use in my 'where' clause.
Thank you for your time.
LIKE Example db<>fiddle
Since the query is a simple YYYY-MM prefixed lookup, use LIKE DATE_FORMAT(NOW(), '%Y-%m-%%') as 2022-03-%. Functioning the same for either DATETIME or VARCHAR column data-types, and is by-far the fastest solution regardless of indexing.
SELECT p.created_time
FROM `freemark-prod-zohocrm`.patients p
WHERE p.created_time LIKE DATE_FORMAT(NOW(), '%Y-%m-%%');
Compare YEAR_MONTH Criteria db<>fiddle
To fix the issue with the original query not returning results, match the criteria column and value functions. However, as a function is called on the column value a full-table scan will be performed.
SELECT p.created_time
FROM `freemark-prod-zohocrm`.patients p
WHERE EXTRACT(YEAR_MONTH FROM p.created_time) = EXTRACT(YEAR_MONTH FROM NOW());
To prevent a full-table scan avoid altering column values in the criteria using DATE_FORMAT(created_time), EXTRACT(... FROM created_time) or other functions, which will cause MySQL to check all rows in the table to determine if the condition matches.
MySQL 5.5 and earlier db<>fiddle
Note: In MySQL 5.5 and earlier, extract(year_month from curtime()) or for any date specific Temporal Intervals will return NULL because curtime() returns the TIME portion as HH:MM:SS.The behavior appears to have changed in MySQL 5.6 and later, where EXTRACT() will apply to the current date when the date argument is supplied as a TIME data-type and failing when supplied as a time string literal.
However, an undesirable value will be returned when using a date + time interval such as DAY_MINUTE and the date portion of the value is omitted.
SELECT
curtime(), /* 19:07:40 */
extract(year_month from curtime()), /* NULL */
extract(day_minute from curtime()); /* 1907 */
To resolve the issue always use NOW(), otherwise in MySQL 5.5 and earlier curtime() should be replaced with CURDATE() or NOW() depending on the interval being used.
DATE Interpreted Example db<>fiddle
As DATE_FORMAT() returns a string literal, to prevent string comparison issues in MySQL such as '10' > '2' = false, enforce a DATE or DATETIME context.
When using DATE or DATETIME interpreted values (see explanation below) to retrieve the rows for an entire month, the following criteria values can be used to force the condition to process in the context of a DATE data-type
DATE(DATE_FORMAT(NOW(), '%Y-%m-01')) to get the first day of the current month as a DATE data-type
LAST_DAY(NOW()) + INTERVAL 1 DAY to get the the first day of the next month as a DATE data-type.
SELECT p.created_time
FROM `freemark-prod-zohocrm`.patients p
WHERE p.created_time >= DATE(DATE_FORMAT(NOW() ,'%Y-%m-01'))
AND p.created_time < LAST_DAY(NOW()) + INTERVAL 1 DAY;
LAST_DAY(NOW()) will return the date as 2022-03-31
+ INTERVAL 1 DAY will increment the date by one day to 2022-04-01
MySQL 5.6+ Results
CREATE TABLE patients_varchar (
`id` INTEGER NOT NULL PRIMARY KEY,
`created_time` VARCHAR(25),
INDEX(`created_time`)
);
INSERT INTO patients_varchar
(`id`, `created_time`)
VALUES
('1', '2022-02-19T15:21:52+08:00'), /* Added to verify range */
('2', '2022-03-19T15:21:52+08:00'),
('3', '2022-03-19T13:10:22+08:00'),
('4', '2022-03-19T13:09:52+08:00'),
('5', '2022-03-19T13:02:47+08:00'),
('6', '2022-03-20T20:51:03+08:00'),
('7', '2022-03-31T20:51:03+08:00'),
('8', '2022-04-20T20:51:03+08:00'); /* Added to verify range */
created_time
2022-03-19T13:02:47+08:00
2022-03-19T13:09:52+08:00
2022-03-19T13:10:22+08:00
2022-03-19T15:21:52+08:00
2022-03-20T20:51:03+08:00
2022-03-31T20:51:03+08:00
VARCHAR and Date Time Literals Explanation
When the column data-type is VARCHAR using a valid date time string literal format such as YYYY-MM-DDTHH:MM:SS+08:00, MySQL will automatically interpret the column value format of YYYY-MM-DDTHH:MM:SS+MM:HH as a DATETIME data-type appropriately when provided a criteria value in the DATE or DATETIME data-type context.Please see the String and Numeric Literals in Date and Time Context for more details.
DATETIME context and time zone offsets
For MySQL 5.6+ to specify the inclusion of a time value, use TIMESTAMP(DATE_FORMAT(LAST_DAY(NOW()), '%Y-%m-%d 23:59:59')) to force a DATETIME context as opposed to using DATE().
For MySQL 5.5 and earlier db<>fiddle, when specifying a DATETIME context, the time zone offset in the column value is not parsed correctly and produces a different result. Using a string context of DATE_FORMAT(LAST_DAY(NOW()), '%Y-%m-%dT23:59:59') resolves the issue but may produce unexpected results, due to the string context comparison eg: '10' > '2' = false.
Note: the T is required for MySQL to parse the YYYY-MM-DDTHH:MM:SS formatted column value correctly.
For example the following conditions will all return true due to the DATETIME context. While MySQL 8.0+ will process the time zone offset when it is included in the string.
SELECT
'2022-02-19T15:21:52+08:00' = TIMESTAMP('2022-02-19T15:21:52'),
'2022-02-19T15:21:52+08:00' < TIMESTAMP('2022-02-20T15:21:52'),
'2022-02-19T15:21:52+08:00' > TIMESTAMP('2022-02-18T15:21:52'),
'2022-02-19T00:00:00+08:00' = TIMESTAMP('2022-02-19'),
'2022-02-19T15:21:52+08:00' < TIMESTAMP('2022-02-20'),
'2022-02-19T15:21:52+08:00' > TIMESTAMP('2022-02-18');
As opposed to the following conditions comparing strings that all return unexpected results.
SELECT
'2022-02-19T15:21:52+08:00' = '2022-02-19T15:21:52', # false
'2022-02-19T15:21:52+08:00' <= '2022-02-19T15:21:52', # false
'2022-02-19T15:21:52+08:00' > '2022-02-19T15:21:52'; # true
querying based on function calls such as extract(), or others datepart(), etc. are not Sargeable
What you would be better doing is something like
where
created_time >= '2022-03-01'
AND created_time < '2022-04-01'
This way, it gets the entire month in question including time portion up to 2022-03-31 # 11:59:59pm.
Now, to compare automatically against whatever the current date IS, you can do with MySQL Variables to compute the first of the month and beginning of next month for your from/to range.
select
...
from
( select #FirstOfMonth := DATE_FORMAT(CURDATE(), '%Y-%m-01'),
#FirstOfNextMonth := date_add( #FirstOfMonth, interval 1 month )) sqlvars,
`freemark-prod-zohocrm`.patients p
where
p.created_time >= #FirstOfMonth
AND p.created_time < #FirstOfNextMonth

Comparing datetime pattern 'y-M-d H:m:s' select condition with Date type

I hava a sql query that says:
SELECT * FROM t_coun_student_sign_1018954616644580 signStu WHERE signStu.is_deleted = 0 AND signStu.student_wid = 1000363408 and signStu.attendance_date >= "2019-12-26 13:00:00.0"
The column attendance_date is a date type and the sql result does not contain attendance_date = 2019-12-26. So I thought that 2019-12-26 13:00:00.0 is not converted to date type like 2019-12-26.
But attendance_date is also an index and the sql query can use the index.
So if 2019-12-26 00:00:00.0 is not converted to date type, how could I make it happen?
What I want to know is that 2019-12-26 00:00:00.0 is to be converted to which type to compare with attendance_date, or what does mysql do when date, datetime, timeStamp type are compared with yyyy-MM-dd HH:mm:ss pattern string format.
what i what know "2019-12-26 00:00:00.0" is convert to which type to compare with attendance_date or what will mysql do when date datetime timeStamp type compare with the 'yyyy-MM-dd HH:mm:ss' pattern string
When operands have different but compatible datatypes in comparing operation then they are converted to the same datatype which guarantees that data loss or change will not occur.
So DATE type value is converted to DATETIME type in such case, and not backward (converting '2019-12-26 13:00:00.0' to DATE will alter the value).
To obtain the result which you need (does the literal is a parameter really?) you may convert the literal to DATA type explicitly:
and signStu.attendance_date >= DATE('2019-12-26 13:00:00.0')
If attendance_date be a date only column, then in the context of comparing it against a timestamp literal, it will behave as a timestamp on midnight of that date. So, assuming there were a record which had an attendance date value of 2019-12-27, then the WHERE clause would become:
WHERE ... AND '2019-12-27 00:00:00' > '2019-12-26 13:00:00.0'
^^^ the attendance "date"
Depending on the logic you want, you may have to change the WHERE clause to use some derivative of the attendance date.
Use "Date" function with column then you are able to compare.
Like- Date(signStu.attendance_date)
Use mysql DATE_FORMAT function for the query like below
DATE_FORMAT(signStu.attendance_date, '%Y-%m-%d %H.%i.%s') >= DATE_FORMAT("2019-12-26 13:00:00.0", '%Y-%m-%d %H.%i.%s')

MySQL - How to select rows where datetime field is not equal to 0000-00-00 00:00:00?

Here is my table "tb_posts":
I want to select only those rows where datetime field i.e. post_date_published is not equal to 0000-00-00 00:00:00. I am using following query but it doesn't work:
SELECT * FROM `tb_posts` WHERE `post_date_published` IS NOT NULL
I am getting the same output as shown in the above picture.
Why IS NOT NULL is not working?
As per the MYSQL documentation it saves invalid dates as '0000-00-00 00:00:00'. It will not be considered as NULL.
Try comparing with the date '0000-00-00 00:00:00':
SELECT * FROM tb_posts where post_date_published != '0000-00-00 00:00:00'
A method I use with this sort of thing is
SELECT `columns` FROM `tb_posts` WHERE UNIX_TIMESTAMP(`post_date_published`) > 0
From the MySQL Documentation:
The valid range of argument values is the same as for the TIMESTAMP
data type: '1970-01-01 00:00:01.000000' UTC to '2038-01-19
03:14:07.999999' UTC. If you pass an out-of-range date to
UNIX_TIMESTAMP(), it returns 0.
The UNIX_TIMESTAMP function forces the result to be an integer so it's much easier to work with in these quick comparisons. It is also vital for working with MySQL 5.7 where "empty" (ie zero value) date/time columns are not allowed.
(I had a lot of grief trying to convert various date columns to NULL because MySQL 5.7+ didn't recognise 0000-00-00 00:00:00 as a valid comparison -- so I converted it to a unix timestamp so as to compare the timestamp rather than the actual [invalid] date.)

Want to run a query which gives me results between two dates. I have timestamp in unix epoch format

I have got timestamps in epoch UNIX format. I want to run a query by directly giving date and not timestamp. How is that possible?
SELECT FROM_UNIXTIME(timestamp)
FROM report_data
WHERE timestamp = '1399376713'
I used this to convert to human readable format.
My database is something like this
timestamp event_type flags
1399357862 701 null
I want to give a particular date in my query and get the result.
It's possible using the FROM_UNIXTIME function.
This assumes that your table contains columns in DATETIME or TIMESTAMP, and you are wanting to supply 32-bit integer values in the query.
For example:
SELECT ...
FROM mytable t
WHERE t.datetime_col >= FROM_UNIXTIME( ? )
AND t.datetime_col < FROM_UNIXTIME( ? )
The integer values supplied as arguments to the FROM_UNIXTIME function will be interpreted as unix-style "seconds since epoch" integer values, and be converted to a DATETIME value using the current timezone setting of the client connection.
This approach will enable MySQL to use a range scan operation using an index with a leading column of datetime_col.
What's not at all clear is what the datatype of your column is, and what values you want to supply in the query. If the columns is datatype DATE, DATETIME or TIMESTAMP (which would be the normative pattern for storing date/time data), then you can specify date literals in standard MySQL format, 'YYYY-MM-DD HH:MM:SS'.
WHERE t.timestamp_col >= '2015-02-11 07:00'
AND t.timestamp_col < '2015-02-11 23:30:00'
If you are storing the "timestamp" as an integer value, then you will need the right side of the predicates to return an integer value, e.g.
WHERE t.integer_col >= UNIX_TIMESTAMP('2015-02-10')
AND t.integer_col < UNIX_TIMESTAMP('2015-02-10' + INTERVAL 24 HOUR)

Is A Datetime Column's Value the Default One

I have a MySQL table and a column of type datetime. It's default value is something like 0000-00-00 00:00:00.
How do I check if a given row's value on this datetime column is the above by using native MySQL query functionality. E.g. without using "SELECT * FROM table WHERE my_date<>'0000-00-00 00:00:00'", because this leaves room for errors on different MySQL servers and configurations I believe.
SELECT * FROM table WHERE my_date <> 0
You can test it with
select cast('0000-00-00 00:00:00' as datetime) = 0
which returns true (and false for all other datetime values).
You could do something like "SELECT * FROM table WHERE my_date > '1970-01-01 00:00:00". 1/1/1970 is the commonly used epoch date.