How do I remove ON UPDATE CURRENT_TIMESTAMP from an existing column? - mysql

I did a dump of a mysql 5.5 database and loaded it into a 5.6 server.
The dump added ON UPDATE CURRENT_TIMESTAMP to a bunch of columns that didn't have it previously.
I'm searching for an ALTER TABLE statement that will remove the ON UPDATE CURRENT_TIMESTAMP rule without making any other changes. In my imagination it should be something like ON UPDATE NOOP or ON UPDATE NO_CURRENT_TIMESTAMP.
ON UPDATE JUST_BE_A_NORMAL_COLUMN?
I tried using the "Clear default" option in mysql workbench and it did the opposite of what it should have done - it gave the column a default!
I was able to get rid of the default with ALTER TABLE t ALTER COLUMN c DROP DEFAULT, so the column is mandatory in INSERTs (just like it was before the dump/reload, as I wanted it) but the unwanted behavior on UPDATEs remains.
I have not enabled the explicit_defaults_for_timestamp option. If I was starting fresh I'd definitely use that option since it seems a lot more sane. But since I already had the columns configured the way I wanted them in 5.5, I expected them to keep the same semantics when transferred to 5.6. Apparently mysqldump just wasn't smart enough.
At this point I'm not sure I understand what effects would result from enabling explicit_defaults_for_timestamp. Would that option change the behavior of existing tables, or does it only change the interpretation of future CREATE TABLE commands? Would turning it on somehow help me fix the broken columns?
UPDATE:
A similar question is here but that one is about creating a new table, not altering an existing column. In fact that question is the one I used as a guide when creating the tables on the 5.5 server. I used the 2-step procedure: create with default 0 to suppress ON UPDATE CURRENT_TIMESTAMP, then drop default.
The 2-step procedure definitely doesn't produce the correct result on the 5.6 server without explicit_defaults_for_timestamp; this is a sign that either 5.6 doesn't perfectly imitate the old behavior in this mode, or the old server never did what I thought it was doing. I can't be sure which.

ALTER TABLE mytable
CHANGE mycolumn
mycolumn TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
I believe this will reset and void the ON UPDATE. This would effectively make this definition:
CREATE TABLE mytable (
# Other Columns
mycolumn timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
)
Change into this one:
CREATE TABLE mytable (
# Other Columns
mycolumn timestamp NOT NULL default CURRENT_TIMESTAMP
)
If you wanted to reset the column entirely, you should be able to simply redefine it like:
ALTER TABLE mytable
CHANGE mycolumn
mycolumn TIMESTAMP NOT NULL;

Using the ideas from the other answers, and a couple of freshly installed mysql server instances, I have done a comparison of the behavior of several different CREATE and ALTER commands on 3 different server configurations:
mysql 5.5.45
mysql 5.6.26 without explicit_defaults_for_timestamp
mysql 5.6.26 with explicit_defaults_for_timestamp
The easiest one to explain is 5.6 with explicit_defaults_for_timestamp. Everything is sane. The timestamp type is not noticeably different from any other type. Columns created before the explicit_defaults_for_timestamp flag was turned on retain their old defaults and magic update.
In 5.5, the implicit defaults happen when a timestamp column is created (if it is the first timestamp column in the table). These are well documented already. The magic update behavior can be avoided by setting an explicit default, and then the default can be removed, leaving the column with the 3 desired attributes: non-nullable, no default, no magic update. This is the result of CREATE TABLE t (TIMESTAMP c NOT NULL DEFAULT 0) and ALTER TABLE t ALTER COLUMN c DROP DEFAULT.
This state can't be recreated with a single CREATE TABLE command, and it doesn't survive a mysqldump.
5.6 without explicit_defaults_for_timestamp is the most interesting case. It's almost the same as 5.5, but the DROP DEFAULT command is different. If you try the "create with default 0 then drop default" sequence, the magic update attribute appears as a side effect of the drop. But if you make the default CURRENT_TIMESTAMP instead of 0, then the DROP DEFAULT works without the side effect. (Must be a bug. I can't imagine any reason it would intentionally behave this way.)
Therefore this pair of commands will have the same result on all of the server configurations I tested:
ALTER TABLE t CHANGE COLUMN c c TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE t ALTER COLUMN c DROP DEFAULT;
The column now has no default and no magic update.

For your use case I think you would be better served with DATETIME, eg:
ALTER TABLE `my_table`
CHANGE `my_col` `my_col` DATETIME NOT NULL DEFAULT NOW();
This will default to NOW() on insert, but remain unaffected on update.
See this question for a good explanation of the difference:
Should I use field 'datetime' or 'timestamp'?

Try enabling the explicit_defaults_for_timestamp system variable and then redefine the columns with:
ALTER TABLE `table` CHANGE COLUMN `col` `col` TIMESTAMP NOT NULL;
If I understand the documentation correctly enabling explicit_defaults_for_timestamp is mandatory to be able to define TIMESTAMP columns declared as NOT NULL and without an explicit DEFAULT.

If you want to remove both the DEFAULT value and ON UPDATE value, nothing but the following helped me
ALTER TABLE `your_table` CHANGE `your_column` `your_column` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00';

Related

What is the standard way to add DEFAULT constraint using ALTER TABLE query?

I want to change the default value of a column after creating the table in MySQL. Which one of the following queries is the standard way to change the default value of the column after creating the table?
ALTER TABLE table_name
MODIFY attribute_name DATATYPE DEFAULT default_value;
ALTER TABLE table_name
ALTER attribute_name SET DEFAULT default_value;
Either works.
If you use MODIFY COLUMN, you must remember to include the column data type and other options that might be present on that column such as NOT NULL, AUTO_INCREMENT, or ZEROFILL. If you forget one of these options, it is removed from the column definition.
This can have unintended side effects. For example, changing the default of a column is a metadata-only change, so it is instant even if the table is huge. But changing a column's nullability to or from NOT NULL requires a table restructure. So if you accidentally change the nullability of the column by leaving that option out, you find yourself waiting for hours when you didn't have to.
If you use ALTER COLUMN ... SET DEFAULT, you don't have to spell out all those column options. They are left unaltered. This is more convenient and less error-prone if you only want to change the default value.
As for which one is standard, the ALTER COLUMN .. SET DEFAULT is in the ANSI/ISO SQL specification. MODIFY COLUMN is a MySQL extension to the standard for the sake of Oracle compatibility.

MySQL error column cannot be null although default value set

I recently moved my SQL Database to another Amazon RDS server with version 5.7.
Before that, the application was working fine but now I started logging errors:
"ER_BAD_NULL_ERROR: Column xyz cannot be null" - The column already has a default value CURRENT_TIMESTAMP
I checked online and people suggested to have the sql_mode equal to NO_ENGINE_SUBSTITUTION
I checked the existing settings and it is already like that.
Any other reason I am getting this error? Any tricks?
Thanks.
After searching more, the problem was only in timestamp fields with current_timestamp default value. I searched in the parameters and found explicit_defaults_for_timestamp that was enabled (value 1) and with a bit more research, I had to disable this parameter as per the documentation here
https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp
in order to get the required result and fix the problem.
Simply deactivate explicit_defaults_for_timestamp
SET GLOBAL explicit_defaults_for_timestamp = 0;
I have no idea why it works like that in this particular case, so I would concentrate on fixing a problem.
According to the docs NO_ENGINE_SUBSTITUTION has nothing to do with the error, during application run. I would select rows with column "xyz" of NULL value and update it to something - not null.
Default is applied when row is created. Let's say you have a table with some millions of rows, and want to add column with not null. That would block your table for significant amount of time. So you can create column without not null, but with default. That operation deals only with metadata, so is fast. Default will deal with all new rows. After that you can slowly update all rows. At the end not null constraint can be added. Not sure if DB is checking constraint when adding it at last step. Or maybe prev. version had problem with it? With MySQL things like that happens.

MySQL Add Column with Online DDL

I'm currently trying to add a column to a table of ~25m rows. I need to have near-0 down time, so was hoping to use online DDL. It runs for a while, but eventually runs into the issue:
"Duplicate entry '1234' for key 'PRIMARY'"
[SQL: u'ALTER TABLE my_table ADD COLUMN my_coumn BOOL NOT NULL DEFAULT false']
I think this is happening because I'm running INSERT ... ON DUPLICATE KEY UPDATE ... operations against the table while running the operation. This seems to be a known limitation.
After this didn't work, I tried using the Percona pt-online-schema-change tool, but unfortunately, because my table has generated columns, that didn't work either with error:
The value specified for generated column 'my_generated_column' in table '_my_table_new' is not allowed.
So, I'm now at a loss. What are my other options for adding a column without blocking DML operations?
Your Alter statement is creating a non nullable column with a default of false. I'd suspect this to place an exclusive lock on your table, attempt to create the column, then setting it to False across each row.
If you don't have any available downtime, I'd suggest you
Add the column as nullable and with no default
ALTER TABLE my_table ADD COLUMN my_coumn BOOL NULL;
Update the values for existing rows to false
update my_table set my_coumn=false;
Alter the table a second time to be not nullable and with a default.
ALTER TABLE my_table modify my_coumn BOOL NOT NULL DEFAULT false;
Alternatively you could use something like Percona which manages schema changes using triggers and is meant to offer the ability to update schemas without locking the table.
Either option I'd suggest you test in your development environment with some process writing to the table to simulate user activity.

How to alter MySQL table without losing data?

In my application, I make some changes and upload them to a testing server. Because I have no access to the server database I run ALTER commands to make changes on it.
Using a method I ran the following command on server:
ALTER TABLE `blahblahtable` ADD COLUMN `newcolumn` INT(12) NOT NULL
After that, I found that the all the data of the table has been removed. Now the table is blank.
So I need to alter the table without removing his data. Is there any way to do that?
Your question is quite obvious. You're adding a new column to the table, and setting it to NOT NULL.
To make things clearer, I will explain the reaction of the server when you run the command:
You add a new column, so every row of the table has to set a value for that column.
As you don't declare any default value, all the rows set null for this new column.
The server notices that the rows of the table have a null value on a column that doesn't allow nulls. This is illegal.
To solve the conflict, the invalid rows are deleted.
There are some good fixes for this issue:
Set a default value (recommended) for the column you're creating.
Create the column without the NOT NULL, set the appropiate values, and then make the column NOT NULL.
You can create a temp table, pass all the information from the table you want to alter, and then return the info to the altered table.

MySQL Timestamp field

Is it possible to set a field to Timestamp but not have it change on update to current timestamp?
I'm trying to do that using phpMyAdmin and it doesn't let me remove the default on update CURRENT_TIMESTAMP
See this question and answer: Support user time zones
I am trying to use the TIMESTAMP as it will allow me to play around with the timezones easily.
Is it not possible to keep the data in that field intact when updating the same row?
This is the behaviour of TIMESTAMP. It can be confusing alright. Read this to work through it. Alternatively consider using a DATETIME.
The server allows any combination of DEFAULT and ON UPDATE, if phpMyAdmin doesn't let you set it, then it's maybe a bug in phpMyAdmin. Anyway, it's important to note that timestamp columns are treated specially in mysql, so if you have more than one of this type in your table, it's well possible that it's not gonna work the way you expect.
From the mysql docs:
In a CREATE TABLE statement, the first TIMESTAMP column can be declared in any of the following ways:
With both DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP clauses, the column has the current timestamp for its default value, and is automatically updated.
With neither DEFAULT nor ON UPDATE clauses, it is the same as DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
With a DEFAULT CURRENT_TIMESTAMP clause and no ON UPDATE clause, the column has the current timestamp for its default value but is not automatically updated.
With no DEFAULT clause and with an ON UPDATE CURRENT_TIMESTAMP clause, the column has a default of 0 and is automatically updated.
Use the command interface.
Looks like you must specify an "DEFAULT CURRENT_TIMESTAMP" attribute on table creation to get that behaviour.
documentation here.