Mysql. Update not null column with null value - mysql

mysql> SHOW COLUMNS from users LIKE 'created_at';
+------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+-------+
| created_at | datetime | NO | | NULL | |
+------------+----------+------+-----+---------+-------+
1 row in set (0.00 sec)
mysql> SHOW VARIABLES LIKE 'innodb_strict_mode';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| innodb_strict_mode | ON |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> UPDATE `users` SET `created_at` = NULL WHERE `users`.`id` = 200;
Query OK, 1 row affected, 1 warning (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 1
mysql> SELECT `created_at` from `users` WHERE `users`.`id` = 200;
+---------------------+
| created_at |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (0.00 sec)
Is it normal behavior? Just warning.

Yes, it can be normal behavior. It depends on server's SQL Mode. It is possible that database is not in strict mode, and zero-dates are allowed.
See more details on this page - Server SQL Modes: STRICT_ALL_TABLES, STRICT_TRANS_TABLES, NO_ZERO_DATE.
Check current SQL mode using this query -
SHOW VARIABLES WHERE variable_name = 'sql_mode';

Related

MySQL statement with invalid data in WHERE clause returns 0 rows instead of failing

Hopefully this is a simple question. I have the following table:
+---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| KEY_COL | int(11) | NO | PRI | NULL | |
| COL2 | int(11) | YES | | NULL | |
+---------+---------+------+-----+---------+-------+
2 rows in set (0.01 sec)
When I do an update on the table using invalid values in the where clause for KEY_COL, it completes successfully and returns 0 rows affected:
mysql> update LGSRV_TAB_DOC set COL2='9' where KEY_COL='A';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
I have my sql_mode set to STRICT_ALL_TABLES, so I was expecting the update to fail since I'm supplying an invalid value for KEY_COL ('A' instead of a numeric). Is there any way I can change this behavior so that the update fails with an error? I'm running version 8.0.11 on RHEL 6.9.

MARIADB - Can SQL convert NULL to an empty string if no default value is provided?

I have a table that has nullable columns:
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
I insert a row with name set to NULL;
INSERT INTO some_table (id, name) VALUES (1, NULL);
Query OK, 1 row affected (0.02 sec)
SELECT * FROM some_table;
+------+------+
| id | name |
+------+------+
| 1 | NULL |
+------+------+
1 row in set (0.01 sec)
If I alter the table's name column to be not-nullable it apparently converts NULL to an empty string:
ALTER TABLE some_table CHANGE COLUMN name name VARCHAR(255) NOT NULL;
Query OK, 1 row affected, 1 warning (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 1
SELECT * FROM some_table;
+------+------+
| id | name |
+------+------+
| 1 | |
+------+------+
1 row in set (0.02 sec)
At this point I would expect an exception to be raised telling me that I have NULL in my dataset and I can not set the column name to NOT NULL.
Is this a configurable option in SQL/MariaDB?
Why is NULL being converted to an empty string?
There is a warning being invoked when altering the table:
SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 |
+---------+------+-------------------------------------------+
1 row in set (0.01 sec)
Version:
SELECT version();
+----------------+
| version() |
+----------------+
| 5.5.62-MariaDB |
+----------------+
1 row in set (0.02 sec)
Apparently, from the documentation for ALTER TABLE, enabling strict mode would prevent your alter statement from succeeding:
This conversion may result in alteration of data. For example, if you shorten a string column, values may be truncated. To prevent the operation from succeeding if conversions to the new data type would result in loss of data, enable strict SQL mode before using ALTER TABLE.
One way to enable strict mode from within MySQL:
SET GLOBAL sql_mode='STRICT_TRANS_TABLES';
See here for other options.
Using 10.3.15-MariaDB-1 on Debian Buster, I cannot reproduce the problem:
MariaDB [foo]> CREATE TABLE some_table(id int(11), name varchar(255));
Query OK, 0 rows affected (0.009 sec)
MariaDB [foo]> INSERT INTO some_table (id, name) VALUES (1, NULL);
Query OK, 1 row affected (0.003 sec)
MariaDB [foo]> SELECT * FROM some_table;
+------+------+
| id | name |
+------+------+
| 1 | NULL |
+------+------+
1 row in set (0.000 sec)
MariaDB [foo]> ALTER TABLE some_table CHANGE COLUMN name name VARCHAR(255) NOT NULL;
ERROR 1265 (01000): Data truncated for column 'name' at row 1
MariaDB [foo]> SELECT * FROM some_table;
+------+------+
| id | name |
+------+------+
| 1 | NULL |
+------+------+
1 row in set (0.000 sec)
MariaDB [foo]> SELECT version();
+-------------------+
| version() |
+-------------------+
| 10.3.15-MariaDB-1 |
+-------------------+
1 row in set (0.000 sec)
If possible, I suggest you update your MariaDB version. It seems very old to me.

MySQL `timestamp` to be updated even with same column value

I am using timestamp columns to one of my table, and using the auto update functionality. Here is my table schema:
mysql> desc user_rides;
+------------+--------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL |
| ride_cnt | int(11) | YES | | NULL | |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_at | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+--------------+------+-----+-------------------+-----------------------------+
5 rows in set (0.02 sec)
What I'm expecting is that,
created_at column to be initialize with the time, the row gets created and
updated_at column to be same as created_at and also updated when any of the columns(basically ride_cnt) get updated.
This works great.
But what I am also expecting is that the updated_at to be updated even if ride_cnt has the same value. So that I can keep a track of when was the last time the row's value fetched and can be ignored for further run.
For example:
The rows with ride_cnt = 0 to be updated with the latest time we ran the update. So that the rows can be ignored for quite sometime to be reinitialize.
Is there any way we can achieve this without passing in timestamp manually?
Edit:
Here what's happening,
mysql> insert into user_ride set user_id=7445, user_ride=0;
Query OK, 1 row affected (0.01 sec)
mysql> insert into user_ride set user_id=7009, user_ride=2;
Query OK, 1 row affected (0.00 sec)
mysql> select * from user_ride;
+----+---------+-----------+---------------------+---------------------+
| id | user_id | user_ride | created_at | updated_at |
+----+---------+-----------+---------------------+---------------------+
| 1 | 7445 | 0 | 2017-06-13 10:44:05 | 2017-06-13 10:44:05 |
| 2 | 7009 | 2 | 2017-06-13 10:44:18 | 2017-06-13 10:44:18 |
+----+---------+-----------+---------------------+---------------------+
2 rows in set (0.00 sec)
mysql> update user_ride set user_ride=0 where id=1;
Query OK, 0 rows affected (0.01 sec)
Rows matched: 1 Changed: 0 Warnings: 0
mysql> select * from user_ride;
+----+---------+-----------+---------------------+---------------------+
| id | user_id | user_ride | created_at | updated_at |
+----+---------+-----------+---------------------+---------------------+
| 1 | 7445 | 0 | 2017-06-13 10:44:05 | 2017-06-13 10:44:05 |
| 2 | 7009 | 2 | 2017-06-13 10:44:18 | 2017-06-13 10:44:18 |
+----+---------+-----------+---------------------+---------------------+
2 rows in set (0.00 sec)
mysql> update user_ride set user_ride=1 where id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from user_ride;
+----+---------+-----------+---------------------+---------------------+
| id | user_id | user_ride | created_at | updated_at |
+----+---------+-----------+---------------------+---------------------+
| 1 | 7445 | 1 | 2017-06-13 10:44:05 | 2017-06-13 10:45:26 |
| 2 | 7009 | 2 | 2017-06-13 10:44:18 | 2017-06-13 10:44:18 |
+----+---------+-----------+---------------------+---------------------+
2 rows in set (0.00 sec)
I wish to call attention to your first update in the scenario you described above:
mysql> update user_ride set user_ride=0 where id=1;
Query OK, 0 rows affected (0.01 sec)
Rows matched: 1 Changed: 0 Warnings: 0
We can see that there was a matching row, but no update actually took place. The reason for this is the update would have resulted in no data actually changing. Hence, the ON UPDATE clause of the updated_at timestamp never kicked in. I can offer two workarounds for your problem. The first, probably the most performant, would be to just manually set the updated_at column to the current timestamp during the update. Hence, from the example above you would use this instead:
update user_ride set user_ride = 0, updated_at = CURRENT_TIMESTAMP where id=1;
This should trigger an actual update of the row, because the timestamp has changed since it was last updated.
Another workaround would be to find a way to ensure that each update will always change some of the data in the given record. Then, the ON UPDATE clause would always be applied.
This may seem like a limitation, but I guess MySQL does not consider a change as having happened to a record if the underlying data itself did not change.
This question is sort of a duplicate of this one:
How to Force UPDATE of MySQL Record When Nothing Is Changing
However, since SO's coverage of this problem is so thin, I thought this answer might be useful to anyone else encountering the same problems.
Add a trigger.
CREATE TRIGGER user_ride_bu
BEFORE UPDATE ON user_ride
FOR EACH ROW
SET NEW.updated_at = NOW();
(For those wondering where the DELIMITER directives are, they aren't needed when a trigger has a single, simple statement).

Column cannot be null when table has a trigger on MySQL 5.7

I do not know if this is expected behaviour or a bug but it seems incorrect. Using MySQL 5.7.9 from the Ubuntu 14.04 ondrej PPA.
Summary: an operation which should raise a warning (inserting an implicit NULL into a not null column) raises an error, only if the table has a trigger defined.
select ##sql_mode;
+------------------------+
| ##sql_mode |
+------------------------+
| NO_ENGINE_SUBSTITUTION |
+------------------------+
1 row in set (0.00 sec)
show global variables like 'sql_mode';
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| sql_mode | NO_ENGINE_SUBSTITUTION |
+---------------+------------------------+
1 row in set (0.00 sec)
show variables like 'sql_mode';
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| sql_mode | NO_ENGINE_SUBSTITUTION |
+---------------+------------------------+
1 row in set (0.00 sec)
create table _test (col_1 varchar(20) not null, col_2 varchar(20) not null) engine=myisam charset=utf8;
Query OK, 0 rows affected (0.00 sec)
show columns from _test;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| col_1 | varchar(20) | NO | | NULL | |
| col_2 | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
insert into _test (col_1) values ('abc');
Query OK, 1 row affected, 1 warning (0.00 sec)
^^^^^^^^^ This is expected behaviour ^^^^^^^^^
delimiter $$
create trigger `insert_test` before insert on `_test` for each row begin set NEW.col_1 = concat(NEW.col_1, '+test'); end$$
Query OK, 0 rows affected (0.02 sec)
delimiter ;
insert into _test (col_1) values ('abc');
ERROR 1048 (23000): Column 'col_2' cannot be null
^^^^^^^^^ This is UNexpected behaviour ^^^^^^^^^
Thus the same command in the same mode has a different end-state when a trigger is set.
Can anyone see a mistake in this, has anyone experienced similar or is this a bug in the release?
Update
Having tested the same process in 5.6.19 (also on Ubuntu 14.04) the final line returns:
insert into _test (col_1) values ('abc');
Query OK, 1 row affected, 1 warning (0.01 sec)
Which once again is expected behaviour.
Update 2
This has been submitted as a bug to the MySQL development team and is now at verified status.

What is the true range of possible values of a field type 'DATE'?

DATE field type has limitations '1000-01-01' -'9999-12-31'.
BUT Why inserted '987-10-10' not '1000-01-01' ?
mysql> INSERT INTO DATE12(datas) VALUES('987-10-10');
Query OK, 1 row affected (0.03 sec)
mysql> SELECT * FROM DATE12;
+------+------------+
| id | datas |
+------+------------+
| NULL | 1987-11-04 |
| NULL | 0987-10-10 |
+------+------------+
2 rows in set (0.00 sec)
incorrect data format
mysql> INSERT INTO DATE12(datas) VALUES('10001-13-12');
Query OK, 1 row affected, 1 warning (0.03 sec)
mysql> SELECT * FROM DATE12;
+------+------------+
| id | datas |
+------+------------+
| NULL | 1987-11-04 |
| NULL | 0987-10-10 |
| NULL | 0000-00-00 |
But for time type all good
mysql> INSERT INTO time1(t) VALUE('-1112:45:12');
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> SELECT * FROM time1;
+------+------------+
| id | t |
+------+------------+
| NULL | NULL |
| NULL | -12:45:12 |
| NULL | -838:59:59 |
+------+------------+
3 rows in set (0.00 sec)
mysql> INSERT INTO time1(t) VALUE('11112:45:12');
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> SELECT * FROM time1;
+------+------------+
| id | t |
+------+------------+
| NULL | NULL |
| NULL | -12:45:12 |
| NULL | -838:59:59 |
| NULL | 838:59:59 |
+------+------------+
4 rows in set (0.00 sec)
mysql>
...Why insered '987-10-10' not '1000-01-01' ?
As per documentation on The DATE, DATETIME, and TIMESTAMP Types
For the DATE and DATETIME range descriptions, “supported” means that although earlier values might work, there is no guarantee.
Meaning, the dates prior to '1000-01-01' may also be accepted, but not guaranteed.
And hence, in your case the date entry '987-10-10' is accepted with no errors.
And you claim that '1000-01-01' is not inserted. But you actually tried '10001-13-12'.
And even if you try with '1000-13-12', it will fail, because, value '13' for 'month' part is meaning less.
As per documentation on Date and Time Literals
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'.
Though not seen in the documentation, the above statement is also valid for format like 'YYYY-MM-DD' and others. And the same is shown in the following example:
mysql> insert into vdt(dt) values( '1001-13-32' );
ERROR 1292 (22007): Incorrect date value: '1001-13-32' for column 'dt' at row 1
mysql> select date_format( '2013-13-32', '%Y-%m-%d' ); show warnings;
+-----------------------------------------+
| date_format( '2013-13-32', '%Y-%m-%d' ) |
+-----------------------------------------+
| NULL |
+-----------------------------------------+
1 row in set, 1 warning (0.00 sec)
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1292 | Incorrect datetime value: '2013-13-32' |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select str_to_date( '20131232', '%Y%m%d' ); show warnings;
+-------------------------------------+
| str_to_date( '20131232', '%Y%m%d' ) |
+-------------------------------------+
| NULL |
+-------------------------------------+
1 row in set, 1 warning (0.00 sec)
+---------+------+---------------------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------------------+
| Warning | 1411 | Incorrect datetime value: '20131232' for function str_to_date |
+---------+------+---------------------------------------------------------------+