Implicit cast from now() to a date field - mysql

I have an issue with MySQL 5.1. A datetime data type isn't implicitly casted to match a date column.
SELECT * FROM my_table WHERE my_date_field = NOW()
This request doesn't return any rows using MySQL 5.1, but works well with version 5.0.
If we use CURDATE() instead of NOW() it works both in MySQL 5.0 and MySQL 5.1.
If the cast is explicit (CAST(NOW() AS DATE)), it also works both in MySQL 5.0 and MySQL 5.1.
The problem only appears with implicit cast from datetime to date. Doesn't anyone already encountered this issue or has a clue about how to solve this problem?
I know it's not the best to use NOW() instead of CURTIME(), but this isn't the question here. It is currently used in an application and the purpose is to avoid rewriting everything.
Thanks!

This was fixed in MySQL 5.1.17 to allow CURDATE() to evaluate to less than NOW() when stored in a DATE column.
Now, when comparing a DATE to a DATETIME, they are compared as DATETIME. When a DATE is cast to a DATETIME, it has a zero hour.
If my_date_field is '2010-01-01' AND NOW() is '2010-01-01 05:01:01', when they are compared, my_date_field is promoted to '2010-01-01 00:00:00', which is obviously less than '2010-01-01 05:01:01'.
Originally, when the left side was a column, the promotion from DATE to DATETIME didn't occur. However, apparently they thought it was more consistent to always promote it.
Sorry, but you just got lucky that it worked before. A date that has a zero hour should evaluate to less than the same date with a non-zero hour.
Unfortunately, there is no way to turn off this "bug fix". Your only solution is to change NOW() to CURDATE() or roll back to a prior version.
Actually, you could compile your own version and either undo the "bug fix" or override the NOW() function.

The behaviour makes sense because NOW() is of the type DATETIME, and CURDATE() of the type DATE.
As for why the variables are cast in one server version, and not in the other - this sounds more like a difference in server modes, i.e. the one instance where the cast fails being more strict than the other.
An interesting point from that document (not sure whether this is your problem but it could be): ALLOW_INVALID_DATES:
This mode is implemented in MySQL 5.0.2. Before 5.0.2, this was the default MySQL date-handling mode. As of 5.0.2, the server requires that month and day values be legal, and not merely in the range 1 to 12 and 1 to 31, respectively. With strict mode disabled, invalid dates such as '2004-04-31' are converted to '0000-00-00' and a warning is generated. With strict mode enabled, invalid dates generate an error. To allow such dates, enable ALLOW_INVALID_DATES.
Anyway, I'm not sure whether it makes sense digging through changelogs trying to find out what changed when. I would tend to make the behaviour work in both situations (i.e., if I understand you correctly, use CURDATE()) and be done with it.

only compare the date part of your datetime column:
SELECT * FROM my_table WHERE DATE(my_date_field) = DATE(NOW())

Related

MySQL Date/Time Functions and Timezone Issue

I'm working on an application that uses created_at and updated_at columns in MySQL tables. The server/system timezone is set to EDT.
Upon INSERT, I'm using the MySQL CURRENT_TIMESTAMP function for the created_at value. MySQL automatically updates the updated_at value whenever he record is updated (ON UPDATE CURRENT_TIMESTAMP). My problem is that the created_at value is entered as UTC and the updated_at value is entered as EDT.
Can someone please help with a fix and explanation of this issue?
We can't help you with a fix because you've not shown any code. Mysql date, time and date time values have no concept of a timezone. Timestamp values use UTC. It is up to you to write your code to deal with these constraints. If your data will always be constrained to a single timezone then just cast any timestamp values to that timezone:
SELECT CONVERT_TZ(created_at, 'GMT', 'EDT'), updated_at
FROM your_table
This gets very messy when you start to use more than one timezone - a better solution is to always store times in UTC and change the representation at the front end (but this can be a bit of a pain if you run on brain damaged operating systems or don't control the server config)

mysql - Need to allow '0000-00-00 00:00:00' dates

I want zero dates to be allowed in MySQL. I have changed the sql_mode to ALLOW_INVALID_DATES,NO_ENGINE_SUBSTITUTION.
I have changed it in /etc/mysql/my.cnf.
Yet, I when I try to insert data I get the error,
Data truncation: Incorrect datetime value: '0000-00-00 00:00:00'
The MySQL version is 5.7.18.
Any ideas on this would be of great help.
To allow zero dates (0000-00-00 00:00:00), you need to remove that restriction.
To remove particular SQL mode (in this case NO_ZERO_DATE), find the current SQL mode:
SELECT ##GLOBAL.sql_mode;
copy the result and remove from it what you don't need (NO_ZERO_DATE)
e.g.:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
to
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
create and open this file:
sudo vim /etc/mysql/conf.d/disable_strict_mode.cnf
and write and past into it your new SQL mode:
[mysqld]
sql-mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
restart MySQL:
sudo service mysql restart
I am going to assume here that you want to have a valid date so that your queries never have to check for NULL.
One way to do this, is to use what I like to call "In perpetuity" date(s).
These are essentially the min/max dates allowable for the DATETIME data type.
In my uses, there were typically "windows" of from - to pairs, but you might only need the minimum date.
From the Mysql manual:
The DATETIME type is used for values that contain both date and time
parts. MySQL retrieves and displays DATETIME values in 'YYYY-MM-DD
HH:MM:SS' format. The supported range is '1000-01-01 00:00:00' to
'9999-12-31 23:59:59'.
So one way that might work for you, is to utilize '1000-01-01 00:00:00' instead of the zero date.
You might want to read about other mode settings, such as strict and NO_ZERO_IN_DATE and NO_ZERO_DATE https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_date
I could solve this by using zeroDateTimeBehavior=convertToNull
My solution when update table
SET sql_mode='';
UPDATE tnx_k2_items
SET created_by = 790
, modified = '0000-00-00 00:00:00'
, modified_by = 0
It should go without saying that you're doing it wrong. The column should be nullable if you intend to store nothing, and you shouldn't try to insert an empty string when you should be using a NULL.
Consider this, though:
If strict mode is not in effect, MySQL inserts adjusted values for invalid or missing values and produces warnings.
But when strict mode is in effect...
For transactional tables, an error occurs for invalid or missing values in a data-change statement when either STRICT_ALL_TABLES or STRICT_TRANS_TABLES is enabled. The statement is aborted and rolled back
http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sql-mode-strict
SELECT ##SQL_MODE; should reveal that you are running with (at least) STRICT_TRANS_TABLES. While this isn't the internal default in 5.6, it is included in the default 5.6 configuration file. You'll want to remove it and restart the server if you want the old behavior.
...although you should consider making your code behave more correctly.
Try enabling this
ALLOW_INVALID_DATES
http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_allow_invalid_dates
Note: I do not recommend doing this. I'm just answering how to do it. In my opinion, it is better to clean up the queries and data, and provide a good default for columns that are non-null.

UNIX_TIMESTAMP() repeated more than once in MySQL request

I need to get current date (and time 00:00:00) epoch timestamp in MySQL request. I want to do this with math expression: (UNIX_TIMESTAMP() - (UNIX_TIMESTAMP() % 86400)).
But this calls UNIX_TIMESTAMP() more than once; so the question is - is there a possibility that two UNIX_TIMESTAMP() functions return different time? What is the workaround in this case?
Or maybe MySQL executes UNIX_TIMESTAMP() once, and replaces all other calls with current timestamp and no workaround needed?
You could use UNIX_TIMESTAMP(NOW()), because NOW() returns the timestamp when the statement began to execute, so it would behave consistently across multiple calls in the same statement.
NOW() returns a constant time that indicates the time at which the
statement began to execute. (Within a stored function or trigger,
NOW() returns the time at which the function or triggering statement
began to execute.)
SELECT #ut:=UNIX_TIMESTAMP(), (#ut - (#ut % 86400)) AS ep;
You may simply use UNIX_TIMESTAMP(), which will return the start time of the current query.
This isn't explicitly documented — the docs make general reference to the stability of "Functions that return the current date or time ..." — but it is so.

mysql now() function

What's the SQL way of doing this:
$now = date('Y-m-d H:i:s', time())
SELECT * FROM table WHERE '$now' > time
Is it:
SELECT * FROM table WHERE now() > time
Your second example is the correct pure SQL method of doing it.
SELECT * FROM table WHERE NOW() > `time`
Although I find it more readable to reverse them, as it seems to make better logical sense to think the value of time is before now`. This really makes no difference though, and is based on my preference.
SELECT * FROM table WHERE `time` < NOW()
There are many more native MySQL date functions you can use in your queries, described in the MySQL documentation.
For example, to compare against 5 minutes ago, use DATE_SUB()
SELECT * FROM table WHERE `time` < DATE_SUB(NOW(), INTERVAL 5 MINUTE)
I don't understand question - first example is calculating date according to your php backend, second using mysql server's builtin function. essentialy effect is the same.
And if you ever considered, now() function in mysql is deterministic (it will replicate correctly to slaves), so even in master-slave environment both snippets of code have no difference, as long as php backend and sql server have synchronized clocks.
just to clarify: of course if your php backend and mysql clocks are not synchronized there will be differences between both snippets of code, but in normal environment this should not happen, at least be irrelevant.

Dealing with Timestamp/Datetime when copying data from Oracle to SQL Server using SSIS

I am using SSIS to copy data from a table in Oracle to a table in SQL Server 2005. There's no transformation required, so it's just a straight line connecting OLE DB Source to OLE DB Destination. Fairly simple and works, but during execution, a record in the Oracle table contains a timestamp value of the year 0002 in encountered, so SSIS errors out due to an oveflow exception.
If I create an extra column in SQL Server with the data type of string, and remap, then that works fine. However, I prefer to keep the Datatime column and use it in my destination. I am OK with replacing the year 0002 with something like 1900 or something like that. So what's the best way to achieve this if-then-else in SSIS?
I usually let Oracle deal with that by using something like this in my source query:
CAST( Coalesce (
CASE
WHEN TO_CHAR(Effective_Date,'yyyy-mm-dd HH24:MI:SS') < '1900-01-01 00:00:00'
THEN TO_DATE('9999-12-31 00:00:00','yyyy-mm-dd HH24:MI:SS')
ELSE Effective_Date
END ,TO_DATE('9999-12-31 00:00:00','yyyy-mm-dd HH24:MI:SS')) AS DATE) AS Effective_Date
This sets a valid but very future date (dictated by the company I am on contract to as the representation of an invalid date in a required date field. You could also use '1900-01-01 00:00:00' instead of '9999-12-31 00:00:00') in cases where the original date is null or less than 1900-01-01 00:00:00. It also avoids post processing by SSIS on the date field.
I realize you have the fix you wanted, but the other method for dealing with issues of this sort is to use a conditional split. You can find and handle all sorts of data issues where you want to remove records from the main flow and do specific handling of them using conditional split. I find it especially handy when I want to be able to tell the vendor about the bad records they sent us so they can fix their data.