Comparing number and timestamp in mysql produce unexpected results - mysql

SELECT 20170930 < TIMESTAMP(20170831);
# 1
SELECT 20170930 > TIMESTAMP(20170831);
# 0
SELECT '20170930' < TIMESTAMP(20170831);
# 0
SELECT '20170930' > TIMESTAMP(20170831);
# 1
Docs:
If one of the arguments is a TIMESTAMP or DATETIME column and the
other argument is a constant, the constant is converted to a timestamp
before the comparison is performed. This is done to be more
ODBC-friendly. This is not done for the arguments to IN(). To be safe,
always use complete datetime, date, or time strings when doing
comparisons. For example, to achieve best results when using BETWEEN
with date or time values, use CAST() to explicitly convert the values
to the desired data type.
It looks like MySQL does not convert Number into Timestamp before the comparison. Did I misunderstand it?

Related

MySQL: comparing datetime with timestamps

I am comparing datetimes with timestamps since MySQL does support that.
Let's say there is a record created at datetime B, and I have two timestamps A and C which A is 2 mins earlier and C is 2 mins later.
MySQL gives: A < B -> 1 (True); B < C -> 0 (False);
How can this be fixed without a explicit conversion?
It turns out that MySQL does not convert an integer to timestamp thought it is stated in there docs.
If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. 
In practice, MySQL converts the DATETIME column to an integer in the following way:
Say the DATETIME column A has value 2021-09-14 15:11:13; when we do SELECT A > 1;, DATETIME is converted to 20210914151113 implicitly.

Mysql implicit conversion when comparing date column and datetime/time string

I have a table with a Date column. It has a row where my_date column's value is 2017-11-24
SELECT * FROM mytable WHERE my_date = '2017-11-24 00:00:00' returns that row.
While SELECT * FROM mytable WHERE my_date = '2017-11-24 00:00:01' does not.
From mysql doc; this page says
If one of the arguments is a TIMESTAMP or DATETIME column and the
other argument is a constant, the constant is converted to a timestamp
before the comparison is performed. This is done to be more
ODBC-friendly. This is not done for the arguments to IN().
If I am interpreting this correctly, in the second query, the constant on right side would be implicitly casted to Date field
The second page for casting says -
Conversion to a DATE value takes fractional seconds into account and
rounds the time part. For example, '1999-12-31 23:59:59.499' becomes
'1999-12-31', whereas '1999-12-31 23:59:59.500' becomes '2000-01-01'.
The example shows 1999-12-31 23:59:59.499 -> 1999-12-31. I understand explicit casting is preferred, but I am wondering why mysql chose to typecast the column value instead of constant or if there's something else happening here.
I don't think you are interpreting the docs correctly. The first page you quote is about comparing a DATETIME or TIMESTAMP to a constant. You are comparing a DATE to a constant, so this logic should apply:
Conversion of DATE values:
Conversion to a DATETIME or TIMESTAMP value adds a time part of '00:00:00' because the DATE value contains no time information.
So it's like comparing '2017-11-24 00:00:00' = '2017-11-24 00:00:01' which sensibly returns false.
Comparing DATEs to DATETIMEs/TIMESTAMPs is similar to comparing an int to a double in that the implicit conversion applies to the less precise value. You would not expect 42 to match 42.1, and likewise you shouldn't expect 2017-11-24 to match 2017-11-24 00:00:01.

MySQL TIMESTAMP to QDateTime with milliseconds

If I use a QSqlTableModel to access a MySQL database I can convert a TIMESTAMP field using the following:
QDateTime dateTime = index(section, column).data().toDateTime();
QString str = dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
So str shows, i.e. 2014-06-22 22:11:44.221.
But I want to access the database using QSqlQuerry, so I do:
QDateTime dateTime = query.value(column).toDateTime();
str = dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
But now I'm missing the milliseconds, str shows 2014-06-22 22:11:44.000. What's the proper way to see the milliseconds?
If I do str = query.value(column).toString(); then I get 2014-06-22T22:11:44.
From this page:
https://dev.mysql.com/doc/refman/5.6/en/datetime.html
A DATETIME or TIMESTAMP value can include a trailing fractional
seconds part in up to microseconds (6 digits) precision. In
particular, as of MySQL 5.6.4, any fractional part in a value inserted
into a DATETIME or TIMESTAMP column is stored rather than discarded.
So, the millisecond is there in MySQL! But the query.value() does not get it - at this point in the Qt history as pointed by #peppe here.
Relating back to the original question: There is no proper way to see the millisecond since the query does not have it. One alternative could be to modify the query, from:
SELECT timestamp FROM table;
to
SELECT DATE_FORMAT(timestamp, '%Y-%c-%e %H:%i:%s.%f') as timestamp FROM table;
And then finish the job with:
QString str = query.value(column).toString();
QDateTime dateTime = QDateTime::fromString(str, "yyyy-MM-dd hh:mm:ss.zzz000");
I got the insight from here.
From MySQL 5.1 documentation:
A DATETIME or TIMESTAMP value can include a trailing fractional seconds part in up to microseconds (6 digits) precision. Although this fractional part is recognized, it is discarded from values stored into DATETIME or TIMESTAMP columns.
It seems like seconds is the best you can do with timestamp.

MySQL varchar timestamp column stored two different ways

I have a table with device data, one of the columns created_ts -> varchar(30)
The problem: this data in this column contains both linux timestamps and varchars for example:
1381148885
and
2012-09-17 22:13:17
How can I query this column for all records with created_ts > 2013-10-01
I'd opt for distinguishing between the string formats (either 'YYYY-MM-DD' or unix timestamp integer) by checking for a dash character.
I'd consider explicitly converting both of those formats to the DATE datatype, using an appropriate conversion. I'd compare the resulting DATE value with the date literal.
Something like this:
WHERE IF(LOCATE('-',t.created_ts,5), -- which format (yyyy-mm-dd or integer)
STR_TO_DATE(t.created_ts,'%Y-%m-%d %T'), -- convert yyyy-mm-dd string to date
FROM_UNIXTIME(t.created_ts) -- convert string as integer to date
) >= '2013-10-01' -- compare to date literal
Another option would be to convert the string column and the date literal to integer values, and do an integer comparison. (Again, two different conversions for the string column, depending on the format.)
NOTE: I included the hh:mm:ss portion in the conversion with the %T.
When no time component is supplied, the time components is assumed to be midnight (zeros) 00:00:00, and that comes into play depending on whether or not we want to consider
'2013-10-01 07:34:55' > '2013-10-01 00:00:00'
OP query has a greater than comparison. I used a greater than or equal to comparison.
This could all be adjusted, depending on the requirements. We want to be aware that if we aren't careful, some values will get "rounded down" to the previous midnight, and then when we do a greater than comparison, what we're really getting is equivalent to >= '2013-10-02'.
My preference is to make it more explicit. It makes it easier for the reader to understand what the query is actually doing.
UPDATE
I had the arguments in the LOCATE function backwards... the string to search for should be the first argument, the string to be searched is second. That's been corrected in the query above.
Something like this:
select * from yourTable
where created_ts > '2013-10-01'
or from_unixtime(created_ts) > '2013-10-01';

Mysql date function not working for less than

I need to get all records those equal and less than 2012-12-28 i used bellow query for this,
booking_time is DATETIME field, and there are records less than 2012-12-28 but it returns zero rows.
does anyone has idea ?
SELECT * FROM ctx_bookings WHERE DATE(booking_time)<=2012-12-28 ORDER BY id ASC
Table filed
+---------------------+
| booking_time |
+---------------------+
| 2012-12-20 03:10:09 |
| 2012-12-25 02:10:04 |
+---------------------+
Please anybody know why is this happening ?
wrap the value with single quote and surely it will work
SELECT *
FROM ctx_bookings
WHERE DATE(booking_time) <= '2012-12-28'
ORDER BY id ASC
SQLFiddle Demo
As documented under Date and Time Literals:
MySQL recognizes DATE values in these formats:
As a string in either 'YYYY-MM-DD' or 'YY-MM-DD' format. A “relaxed” syntax is permitted: Any punctuation character may be used as the delimiter between date parts. For example, '2012-12-31', '2012/12/31', '2012^12^31', and '2012#12#31' are equivalent.
As a string with no delimiters in either 'YYYYMMDD' or 'YYMMDD' format, provided that the string makes sense as a date. For example, '20070523' and '070523' are interpreted as '2007-05-23', but '071332' is illegal (it has nonsensical month and day parts) and becomes '0000-00-00'.
As a number in either YYYYMMDD or YYMMDD format, provided that the number makes sense as a date. For example, 19830905 and 830905 are interpreted as '1983-09-05'.
As #Barmar commented, your literal expression 2012-12-28 is evaluated as the arithmetic (2012 - 12) - 28, which equals 1,972.
Per #JW.'s answer, you can quote that expression to obtain a valid date literal (of the first form, above). Alternatively:
whilst still quoting the literal, you could use any other punctuation character (or even no character) as the delimiter between date parts:
WHERE DATE(booking_time) <= '2012_12_28'
WHERE DATE(booking_time) <= '20121228'
you could remove the delimiters and leave your literal unquoted:
WHERE DATE(booking_time) <= 20121228
Note also that using a filter criterion like this, which uses a function (in this case, the DATE() function) over a column, requires a full table scan in order to evaluate that function—it therefore will not benefit from any indexes. A more sargable alternative would be to filter more explicitly over the range of column values (i.e. times) that satisfy your criteria:
WHERE booking_time < '2012-12-28' + INTERVAL 1 DAY
This is equivalent because any time that falls strictly prior to the following day will necessarily have occurred on or before the day of interest. It is sargable because the column is compared to a constant expression (the result of the + operation being deterministic), and therefore an index over booking_time can be traversed to immediately find all matching records.
SELECT * FROM ctx_bookings WHERE DATE(booking_time)<='2012-12-28' ORDER BY id ASC
try this mate