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

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.

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.

Update a field in SQL

I have several tables that have a common field (column) called LastName in a MySQL database. Several of the rows in these tables are in mixed case so they don't get selected properly when doing a SELECT.
How can I convert those columns to all UPPER CASE? I can easily handle any new entries to convert them to upper case, but the existing records I'm not so sure about.
would do the job
update table set LastName=UPPER(LastName);
NOTE - if you are running from MySQL workbench you may have to disable safety mode or add a where clause (eg WHERE id>0) otherwise it wont run.
this would work:
UPDATE table_name SET `column_name` = UPPER( `column_name` )
You can use the string function UPPER() to make the column value to upper
update Your_table set LastName=UPPER(LastName)

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

Update mysql table with same values and still get a timestamp update

So I have this stamp timestamp DEFAULT NOW() ON UPDATE NOW() row on my table, and I need it to update even when the update I'm executing is basically same data on all fields.
Is there any way of doing this within the declaration of the table, like some other option other than on update, or do I have to force a stamp = now() every time I update (and remove the on update of course since it will be useless).
I've seen this thread, but it only answers what is happening and why, not how to get around it other than forcing it indirectly
As #veeTrain said, it'd be easy if you added it to your update statement. This is just another way of doing it, you can also use unix_timestamp().
UPDATEtable_nameSETlast_logged_in= unix_timestamp() WHEREid= '$user_id'
I know my response is late, but I ran into a similar issue and figured I'd share my solution for those encountering this thread in the future.
You'd have to use a trigger to force it each time.
DELIMITER GO
CREATE TRIGGER `mydb`.`mytable_U` BEFORE UPDATE ON `mydb`.`mytable`
FOR EACH ROW
BEGIN
SET NEW.stamp = CURRENT_TIMESTAMP;
END
GO
DELIMITER ;
I find it easier to just do the following as part of your update statement:
UPDATE table_name SET last_logged_in = NOW() WHERE `user_id`...
This way the value is always updated and if nothing else has changed the timestamp still gets updated. Thanks for your question; it was the same as mine.

MySQL: Is there a way to automatically set datetime field to record creation timestamp?

I know there is TIMESTAMP data type that automatically updates to timestamp value when a record is updated, and I already have such column.
Besides that I'd like to have a column that automatically populates to NOW() (or CURRENT_TIMESTAMP) and never changes, but MySQL DEFAULT doesn't appear to support function calls.
Please post only pure MySQL answers. I know how to do it at application level.
EDIT: If there's no such feature - I'd appreciate to hear that.
EDIT2: MySQL version is 5.0.32
Use a trigger to set the default.
DELIMITER |
CREATE
TRIGGER trigger_name BEFORE INSERT ON tbl_name FOR EACH ROW
BEGIN
SET NEW.colname = NOW();
END;
|
Try this one, including the delimiter.
I'd like to have a column that automatically populates to NOW() (or CURRENT_TIMESTAMP) and never changes
By saying you'd like it to populate to NOW() it sounds like you're referring to the initial INSERT. If that's the case, can you just use that? Something like
INSERT INTO table (field1,field2,my_datetime) VALUES (1,'a',NOW())