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

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.

Related

How to deal with 0000-00-00 00:00:00 datetimes in MySQL 5.7 without changing SQL mode

I've recently switched to MySQL 5.7 which causes troubles around datetimes. I have datetimes in my database with the default value of 0000-00-00 00:00:00. MySQL 5.7 does not seem to like that type of default value. I understand I can set sql_mode = '' but let's say I don't want to do that (sticking with the default strict mode), what would be the best approach?
I could first update all values:
UPDATE mycolumn SET field_datetime = NULL WHERE field_datetime='0000-00-00 00:00:00'
And then edit the datetime field to use NULL as default.
Would that be future proof? I assume MySQL 5.7 has its reasons to disallow
0000-00-00 00:00:00.
NULL as a value for any empty field is future proof and rather sane. Your application code will need to be tested to sure that it handles NULL correctly.
Accepting 0000-00-00 00:00:00 as valid datetime was a poor choice from MySQL developers years ago and just covered problems rather than solving them. Getting stricter on SQL seems to be the approach MySQL are slowly taking even if application developers are needing to update their structures and code to be more correct (and possibly fixing hidden bugs).

str_to_date not working on one database while working fine on another

I have 1 live database on a server (v5.5.58) and one local on my computer (v8.0.11) that is used as a testing database.
SELECT STR_TO_DATE("12:21:21", "%H:%i:%s")
Running this query on the server works fine while the same query locally returns date as null and a warning saying:
Warning: #1411 Incorrect datetime value: '12:21:21' for function str_to_date
I know that STR_TO_DATE works on MySql version 3.23 so it should work on version 8, right?
The ##SQL_MODE setting differs between your servers. Since 5.6, MySQL Server has used more strict default values for ##SQL_MODE.
If the NO_ZERO_DATE or NO_ZERO_IN_DATE SQL mode is enabled, zero dates or part of dates are disallowed. In that case, STR_TO_DATE() returns NULL and generates a warning
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_str-to-date
This same information appears in the documentation for previous versions.
You can often change newer servers to emulate the (usually permissive, sometimes wrong) behavior of older servers by changing the ##SQL_MODE to match, but do that with due discretion, since the more strict behavior is usually also more correct and helps avoid silent coercion of invalid values.

MySQL Configuration to auto truncate decimal value

I've a table with a field DECIMAL(9,2) and I've 2 servers running both mysql 5.7
If I run this code
INSERT INTO `money_accounts` (`balance`) VALUES (9999999999.99);
On one of the servers it got inserted and value is truncated, on the other it raise a Out of range value for column balance error.
My question is, what is the configuration value that makes this happen? or why it's happening in one server and not in the other?
The definition
DECIMAL(9,2)
means 9 digits of total precision, with 2 digits after the decimal place. The value
9999999999.99
has 12 total digits of precision, with 2 after the decimal place. Hence, the value is out of range. Rightfully, MySQL should have thrown an out of range error in both cases. In the case where it "worked," my guess is that truncation occurred.
By the way, you should be using DECIMAL(12,2) or wider to store the value in your question.
Update:
One possible explanation for why one of your servers was doing the insertion while the other failed is that the first has traditional mode turned off. Run the following on both servers:
SELECT ##SESSION.sql_mode
If you see output looking like
STRICT_TRANS_TABLES, STRICT_ALL_TABLES, ...
then the server is running in traditional mode, which means it won't truncate but will reject. To turn it off, try running
SET SESSION sql_mode=''
and them the insert should succeed (with truncation). But in any case, you should not be relying on truncation in production. If you need more precision, then widen the column.
Reference: Automatically trimming length of string submitted to MySQL
If we read the documentation: http://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html
When MySQL stores a value in a numeric column that is outside the
permissible range of the column data type, the result depends on the
SQL mode in effect at the time
If strict SQL mode is enabled, MySQL rejects the out-of-range value
with an error, and the insert fails, in accordance with the SQL
standard.
If no restrictive modes are enabled, MySQL clips the value to the
appropriate endpoint of the range and stores the resulting value
instead.
So it means that You should set necessary sql mode that will not fail when out-of-range error happens.
There is no config parameter about that.
And resolution of same issue we can see here that says that disabling STRICT_TRANS_TABLES and STRICT_ALL_TABLES modes can fix Your problem.

mysql datetime default current timestamp

Recently i was exporting one of my database from localhost to a production environment and when i tried to plonk in the .sql file, the database that i was inporting the data into returned me an error on the datetime field which is DEFAULT CURRENT_TIMESTAMP. From what i read online, older sql phpadmin versions do not support datetime fields as default current timestamps but rather i must use timestamps.
However, thinking that my entire platform has been structured to suit datetime, is there anything i can do without needing to find a host that supports newer sql versions? Thanks!
Issue may be due to 'SQL_MODE' set to 'TRADITIONAL'.
Setting the SQL_MODE to either STRICT_TRANS_TABLES or ALLOW_INVALID_DATES would resolve the default values issue for datetime and timestamp type of columns.
Refer to MySQL Documentation:
Important SQL Modes
STRICT_TRANS_TABLES
TRADITIONAL
ALLOW_INVALID_DATES

Implicit cast from now() to a date field

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())