the default value must be a constant; why? - mysql

in this said
the default value must be a constant; it cannot be a function or an
expression
can you tell me why ? why we must give constant default value?

This is a limitation in MySQL.
You can either use another RDBMS or get around the problem using a trigger.
CREATE TRIGGER yourtable_insert BEFORE INSERT ON `yourtable`
FOR EACH ROW SET NEW.youraddedcolumn = NOW(), NEW.yourupdatedcolumn = NOW();

One reason I can think of is ambiguity. Should the expression be evaluated before storing it as a default or for each INSERT?

Note that there's one non-constant value you can use in a table definition:
stamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
This can only be used on a TIMESTAMP column, but it's useful enough.

Related

SQL: Set default value to NULL for all columns without default value

The webhosting I use has enabled StrictMode for the Databases. All my php scripts now stopped working because they report I haven't defined a default value for some columns.
As I have a lot of columns in a lot of tables, is there a way to set all the columns with "default value = none" with "default value = NULL" ?
In this way it won't report me the error anymore.
Of course, If there's another (better) way, I am available for it.
I tried looking on the net, but I couldn't find anything suitable for this case.
you can alter column
ALTER TABLE table_name
MODIFY COLUMN col datatype DEFAULT null
A general approach here which should work for each column causing an error would be to set a default value, and then maybe do an update to backfill records missing a value.
ALTER TABLE yourTable ALTER some_text_column SET DEFAULT 'None';
And here is the update:
UPDATE yourTable SET some_text_column = 'None' WHERE some_text_column IS NULL;
You are not required to do this update, but it might make sense to bring older records missing values in line with what newer records would look like.

MySQL MD5(CURRENT_TIMESTAMP) On Update

I'm creating a user registration form and when I get to confirming the user's email by emailing them, I need some sort of unique string to confirm against.
Instead of generating one in PHP and inserting it into the database, I wanted to try and add a column to my table that would hold a unique value that I could use whenever I needed to confirm something.
What I want to do is set the value to an MD5 of the current timestamp. I tried just doing SELECT MD5(CURRENT_TIMESTAMP) in phpMyAdmin just to see if it would let me and it did so I thought I'd add that to an update condition but it doesn't seem to be letting me.
ALTER TABLE users ADD confirmation VARCHAR(40) DEFAULT NULL ON UPDATE MD5(CURRENT_TIMESTAMP);
The above is what I've tried. I get an error and I don't know how else to do it.
Is there anyway I can do this or something similar? Side question, does the ON UPDATE trigger on a row that just got inserted?
The syntax you are trying to use doesn't exist. It looks like you are thinking of ON UPDATE CURRENT_TIMESTAMP but that is a rather specific command, as per https://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html
Use of DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP is
specific to TIMESTAMP.
So the ON UPDATE clause only works with CURRENT_TIMESTAMP and only on fields of type TIMESTAMP.
If you want to use the MD5 of the current timestamp either set a trigger, or just manually set the value (e.g. UPDATE users SET confirmation=MD5(CURRENT_TIMESTAMP()) WHERE user_id=123).
Bare in mind that the MD5 of the current timestamp is something that could be quite easily guessed / brute forced, so don't rely on it for security.
Use a universal unique identifier for this purpose. It's a 128-bit unique number; it's designed for this kind of thing.
It has a string representation that fits in 36 bytes.
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
You can generate these things in most programming languages. In MySQL you use the UUID() function to get one. Every time you call UUID(), you're guaranteed to get a new value.
Add a CHAR(36) column to your database, or just use your VARCHAR(40) column.
You can't use data definition language (ALTER TABLE) to declare ON UPDATE except for a native timestamp. You'll need application code to set your UUID values, just like you do for MD5(CURRENT_TIMESTAMP).

Dynamically generate field value

Is there any way, in MySQL, to dynamically generate a field value? For example a field that stamps the exact date and time that a row was inserted.
You could just get the interface to do it (i.e. PHP) but in a case where you have more than one handler it would be safer if I could get MySQL to do it independently.
The MySQL TIMESTAMP data type has a special default called CURRENT_TIMESTAMP that does this.
Read all about it:
http://dev.mysql.com/doc/refman/5.1/en/timestamp.html
EDIT:
For the more generic case, use a trigger. You can set the value on an insert using a BEFORE INSERT triggers, and likewise you can set the value on updates using a separate BEFORE UPDATE trigger:
http://dev.mysql.com/doc/refman/5.1/en/create-trigger.html

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.

Blocking '0000-00-00' from MySQL Date Fields

I have a database where old code likes to insert '0000-00-00' in Date and DateTime columns instead of a real date. So I have the following two questions:
Is there anything that I could do on the db level to block this? I know that I can set a column to be not-null, but that does not seem to be blocking these zero values.
What is the best way to detect the existing zero values in date fields? I have about a hundred tables with 2-3 date columns each and I don't want to query them individually.
Followup:
The default is already set to null. A long time ago, the default was '0000-00-00'. Some code still explicitly places '0000-00-00'. I would prefer to force that code to throw an error so I could isolate and remove it.
Is there anything that I could do on the db level to block this?
Yes, enable the NO_ZERO_DATE mode:
SET sql_mode = 'NO_ZERO_DATE';
The behaviour is documented. Additionally, you might want to also set the mode to include NO_ZERO_IN_DATE...
Also make sure the sql_mode includes either STRICT_ALL_TABLES or STRICT_TRANS_TABLES; without these NO_ZERO_IN_DATE only give a warning, but insert still succeeds.
What is the best way to detect the existing zero values in date fields? I have about a hundred tables with 2-3 date columns each and I don't want to query them individually.
Separate columns means they have to be checked individually--nothing you can do about that.
Assuming you can't easily fix the data and "SET sql_mode = 'NO_ZERO_DATE';", you could create a view on the table...
CREATE VIEW filter AS
SELECT other_column,
CASE
WHEN realtable.dodgy_date = 0 THEN NULL
ELSE realtable.dodgy_date
END AS dodgy_date
FROM realtable;
SET SQL_MODE='ALLOW_INVALID_DATES';
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'NO_ZERO_DATE',''));
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'NO_ZERO_IN_DATE',''));
Just run above queries on database. It will solve the issue permanently.
If it doesn't matter what date goes in there (ie as long as it's non-zero) you can change the column definition to use NOW() as the default. Probably not an ideal solution, but it does satisfy the criteria :
1) Not-null
2) Non-zero
I'm actually really not proud of that suggestion
You could make the columns nullable and have NULL as the default value, but it sounds like you already have that and it's not working. ALTHOUGH... it could be the tool you're using to display the data doesn't like displaying NULL dates... what tool are you using? Or is the '0000-00-00' showing up in data retreived by code?
You could set a default value that is non-null and also easily recognizable as a default such as 1900-01-01 (assuming you don't normally deal with dates that are close to this date).
A trigger can be used to enforce values for columns.
set the timestamp by default is maybe an option for you, use table change statement for that:
ALTER TABLE mytable CHANGE date_update timestamp NOT NULL DEFAULT CURRENT_DATE()
MySQL CURRENT_DATE() documentation at w3c resource
To remove Zero Dates and replace them by e.g. the current date do this:
UPDATE mytable SET date_update = CURRENT_DATE() where date_update = "0000-00-00"
This is the trigger I use:
delimiter //
CREATE TRIGGER bad_date BEFORE INSERT ON some_table
FOR EACH ROW
BEGIN
IF NEW.the_date='0000-00-00' THEN
SET NEW.the_date= CURDATE();
END IF;
END;//
If updates are a concern, add a separate trigger (BEFORE UPDATE) to do the same thing.