We are migrating our application from MySQL 5.5 to 5.7. As the default value 0000-00-00 is not allowed anymore for date fields in MySQL 5.7 in strict mode, I would like to change the default value to NULL.
The concerned fields are defined as follows:
+------------------+----------------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+----------------------+------+-----+------------+----------------+
| event_start_date | date | YES | | 0000-00-00 | |
| event_end_date | date | YES | | 0000-00-00 | |
+------------------+----------------------+------+-----+------------+----------------+
When I try to execute the following ALTER query:
ALTER TABLE events CHANGE event_start_date event_start_date date DEFAULT NULL;
ALTER TABLE events CHANGE event_end_date event_end_date date DEFAULT NULL;
I get the following error:
Invalid default value for 'event_end_date'
I know it would be possible to disable strict mode, but that is not the solution I am looking for. Strangely enough the same query worked for an other table.
Anyone has an idea what is going wrong?
The error happens already in your query on the first line. There you are trying to change the column event_start_date, the error message however is for column event_end_date. You need to change both columns with a single query in order to avoid this error:
ALTER TABLE events CHANGE event_start_date event_start_date date DEFAULT NULL, CHANGE event_end_date event_end_date date DEFAULT NULL;
It probably worked with your other table because you only had one column of type date.
This is the new strict mode in MySQL 5.7. The default SQL_MODE in MySQL 5.7 is:
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
The best way is to change the schema as #cansik suggested.
You can also remove NO_ZERO_IN_DATE,NO_ZERO_DATE from sql_mode (not recommended but can be temporary workaround)
set global sql_mode="... choose which modes you need ... "
Related
I am new in nodejs and my english is bad please help me, I am just trying to migrate my database in nodejs(express) using knex, and I'm using Mysql for database. I want to rename one field in table, and when I try to migrate my database, I got some problem that say error default value.
here is what I'm trying to do :
My migrate
exports.up = function(knex) {
return knex.schema.table ('tbl_skills', function(table) {
table.renameColumn('preminum_price', 'premium_price')
})
};
Here my database structure
Name | Datatype | length | Default |
id | INT | 20 | No default |
preminum_price | DOUBLE | 5,2 | No default |
insertdate | TIMESTAMP| | 0000-00-00 00:00:00 |
updatedate | TIMESTAMP| | 0000-00-00 00:00:00 |
and this what I got when I'm trying knex migrate:latest
migration file "20191125105226_alter_tbl_skills.js" failed
migration failed with error: alter table `tbl_skills` change `preminum_price` `premium_price` double(5,2) NOT NULL - ER_INVALID_DEFAULT: Invalid default value for 'insertdate'
Error: ER_INVALID_DEFAULT: Invalid default value for 'insertdate'
I dont know to how set value for insertdate with default value. Please help
That is most probably because of server SQL Mode - NO_ZERO_DATE.
In strict mode, don't allow '0000-00-00' as a valid date. You can still insert zero dates with the IGNORE option. When not in strict mode, the date is accepted but a warning is generated. If you have access to my.ini (mysql conf file) remove the NO_ZERO_DATA from sql-mode and restart the server.
You can check it with SHOW VARIABLES LIKE 'sql_mode'
I have a column in my database called time. the type of this column is timestamp and Default value is CURRENT_TIMESTAMP
But after some inserts, in phpMyAdmin it shows the value as datetime, e.g. 2019-05-05 04:24:45 and even the Timezone is shown there and can be changed!
I thought MySQL's timestamp is 4 bytes (compared to 8 bytes of datetime) and doesn't store timezone and data is same as INT(10) such as: 1557094115 (seconds passed since 1970 or something like that)
Can any one please explain this, is it a bug or something?
MySQL version 5.7.25
Edit 1 (Screenshots):
It is a TIMESTAMP column, with default value of CURRENT_TIMESTAMP
As you see it is shown as DATETIME and I cannot compare it with integer value of unix_timestamp... also we can change TimeZone to any value (I thought timestamp doesn't store timezone...)
Edit 2:
If (based on one answer) MySQL stores it as an integer internally, then why can't I compare it with integers? (the following query won't work)
DELETE FROM `table` WHERE time < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL :days DAY))
SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '1555980012' for column 'time' at row 1
I also tried it in Sequel Pro and MySQLWorkbench with same results
If you need to see 1557094115, then apply the function UNIX_TIMESTAMP() to the TIMESTAMP or DATETIME column. It's inverse is FROM_UNIXTIME().
mysql> SELECT UNIX_TIMESTAMP("2019-05-05 04:24:45"), FROM_UNIXTIME(1557055485);
+---------------------------------------+---------------------------+
| UNIX_TIMESTAMP("2019-05-05 04:24:45") | FROM_UNIXTIME(1557055485) |
+---------------------------------------+---------------------------+
| 1557055485 | 2019-05-05 04:24:45 |
+---------------------------------------+---------------------------+
More
The internal storage for TIMESTAMP is 1557055485 in UTC; the timezone is added/removed as it is fetched/stored.
The internal storage for DATETIME is (logically, but not actually) the string "2019-05-05 04:24:45" with no hint of timezone. (Actually, it is packed into 5 bytes in some fashion.)
Without any conversion function, fetching TIMESTAMP and DATETIME look the same:
CREATE TABLE `dtts` (
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`dt` datetime DEFAULT NULL,
`just_date` date NOT NULL,
`di` int(11) DEFAULT NULL,
`ts_int` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci
1 row in set (0.00 sec)
mysql> select * from dtts
-> ;
+---------------------+---------------------+------------+------------+------------+
| ts | dt | just_date | di | ts_int |
+---------------------+---------------------+------------+------------+------------+
| 2017-06-26 17:52:53 | 2011-06-08 20:45:55 | 2011-06-08 | 20110608 | 1465404577 |
| 2017-06-26 17:52:53 | 2013-03-10 02:35:47 | 2013-03-10 | 20130310 | 1465404577 |
Adding NOW() to both, then SELECTing:
mysql> INSERT INTO dtts (ts, dt) VALUES (NOW(), NOW());
Query OK, 1 row affected, 1 warning (0.00 sec)
| 2019-05-08 14:14:07 | 2019-05-08 14:14:07 | 0000-00-00 | NULL | NULL |
+---------------------+---------------------+------------+------------+------------+
DateTime doesn't store timezone information (it's value only), while MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and convert from UTC to the current time zone for retrieval. What you are seeing from PhpMyAdmin is the retrieved value, not stored value.
Since MySQL 5.6.4, the storage of DateTime has been improved from 8 bytes to 5 bytes (+ fractional seconds storage) Reference
Since MySQL 5.6.4, the DATETIME field requires 5 bytes + 3 bytes fractional. The TIMESTAMP type requires 4 bytes + 3 bytes fractional. Neither of these data types store time zone information. However, both MySQL and phpMyAdmin display TIMESTAMP fields according to the timezone of the database server. You can retrieve the database server's timezone info with the following statements:
SELECT ##global.time_zone, ##session.time_zone;
SELECT EXTRACT(HOUR FROM (TIMEDIFF(NOW(), UTC_TIMESTAMP))) AS `timezone`
If you would like phpMyAdmin to display a different timezone from the database server, you can set the SessionTimeZone property inside of phpMyAdmin's config.inc.php file.
Our database uses '0000-00-00 00:00:00' as the default value for many datetime and timestamp fields. MySQL has apparently decided that they only want us to use a valid date or null for these types of fields.
However, the '0000-00-00 00:00:00' values used to be acceptable and our code checks for this value. When I setup a new server, I edit the /etc/mysql/mysql.conf.d/mysqld.cnf file and add one line to the [mysqld] section.
sql_mode=NO_ENGINE_SUBSTITUTION
Today I have attempted to setup a new server. I added the sql_mode=NO_ENGINE_SUBSTITUTION to the MySQL configuration and restarted the mysql service. However, this new server only gets errors.
UPDATE example_table SET active = 1 WHERE example_table_id = 1;
ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'my_date_field' at row 1
I could of course update the database to have NULL values or a "valid" default date such as '1970-01-01 00:00:01', but this would break existing code that checks the data for '0000-00-00 00:00:00'.
Additional Example Information:
Server type: MySQL
Server version: 5.7.22-0ubuntu0.16.04.1 - (Ubuntu)
Protocol version: 10
innodb_version: 5.7.22
SELECT my_date_field FROM example_table WHERE active = 1;
+---------------------+
| my_date_field |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
DESC example_table;
+------------------------------------+--------------+------+-----+---------------------+
| Field | Type | Null | Key | Default |
+------------------------------------+--------------+------+-----+---------------------+
| my_date_field | datetime | NO | | 0000-00-00 00:00:00 |
| active | tinyint(2) | NO | | 1 |
+------------------------------------+--------------+------+-----+---------------------+
I've got other machines on the same version of MySQL working with just sql_mode=NO_ENGINE_SUBSTITUTION and I'm almost to the point of just imaging one of those for this new machine, until such time as the code is updated to look for a valid date or null instead of '0000-00-00 00:00:00'.
The NO_ENGINE_SUBSTITUTION SQL mode doesn't have anything to do with dates. It only did the trick for you because by setting it you also removed the server's default mode, which in modern versions is something on the line of TRADITIONAL, which is a combination mode that, among others, includes NO_ZERO_IN_DATE and NO_ZERO_DATE.
You possibly just want to set a legacy mode for this application at session level, e.g.:
SET ##SESSION.sql_mode = '';
I have two columns in a mysql table that are set by user preference:
custom_string VARCHAR(255) NOT NULL
items_per_row TINYINT UNSIGNED NOT NULL DEFAULT '5'
EXAMPLE:
+--------------------------------------+
| preferences |
+--------------------------------------+
| id | custom_string | items_per_group |
+--------------------------------------+
| 1 | TRINKETS | 8 |
+--------------------------------------+
| 2 | | 5 |
+--------------------------------------+
| 3 | MYSTUFF | 7 |
+--------------------------------------+
items_per_row is a required field. The custom_string field is optionally used to personalize the way grouped items in the list are displayed.
The user is able to update these preferences any time. Here is a crude example of how items might be displayed:
I am wanting to find a way to constrain the length of each user's custom_string so that if it is not blank it must have a length that is exactly the same as the corresponding items_per_group value. I can easily validate the user input with both javascript and PHP and prevent the data from being entered into the database if it doesn't comply with this requirement, however, is there a way to set this constraint within mysql so that an attempt to have an 'invalid' string would be rejected?
MySQL does not implement check constraints. With them, this would be easy:
alter table preferences add constraint chk_preferences_custom
check (custom_string is null or length(custom_string) = items_per_group);
Your only option in MySQL is to use a trigger for this purpose.
In practice, it might be simpler to check at the application level when you insert/update custom_string.
Delphi XE2 and MySql.
My previous question led to the recommendation that I should be using MySql's native TIMESTAMP datatype to store date/time.
Unfornately, I can't seem to find any coding examples, and I am getting weird results.
Given this table:
mysql> describe test_runs;
+------------------+-------------+------+-----+---------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+-------------+------+-----+---------------------+-------+
| start_time_stamp | timestamp | NO | PRI | 0000-00-00 00:00:00 | |
| end_time_stamp | timestamp | NO | | 0000-00-00 00:00:00 | |
| description | varchar(64) | NO | | NULL | |
+------------------+-------------+------+-----+---------------------+-------+
3 rows in set (0.02 sec)
I woudl like to :
declare a variable into which I can store the result of SELECT CURRENT_TIMESTAMP - what type should it be? TSQLTimeStamp?
insert a row at test start which has start_time_stamp = the variable above
and end_time_stamp = some "NULL" value ... "0000-00-00 00:00:00"? Can I use that directly, or do I need to declare a TSQLTimeStamp and set each field to zero? (there doesn't seem to be a TSQLTimeStamp.Clear; - it's a structure, not a class
upadte the end_time_stamp when the test completes
calcuate the test duration
Can somene please point me at a URL with some Delphi code whcich I can study to see how to do this sort of thing? GINMF.
I don't know why you want to hassle around with that TIMESTAMP and why you want to retrieve the CURRENT_TIMESTAMP just to put it back.
And as already stated, it is not a good advice to use a TIMESTAMP field as PRIMARY KEY.
So my suggestion is to use this TABLE SCHEMA
CREATE TABLE `test_runs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`start_time_stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`end_time_stamp` timestamp NULL DEFAULT NULL,
`description` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
);
Starting a test run is handled by
INSERT INTO test_runs ( description ) VALUES ( :description );
SELECT LAST_INSERT_ID() AS id;
and to finalize the record you simply call
UPDATE test_runs SET end_time_stamp = CURRENT_TIMESTAMP WHERE id = :id
just declare a TSQLQuery (or the correct component for the data access layer of your choice), attach it to a valid connection and populate it's SQL property with:
select * from test_runs;
double click on the query to launch it's fields editor and select add all fields from the contextual menu of that editor.
It will create the correct field type, according to the data access layer and driver you're using to access your data.
Once that's done, if you need to use the value in code, usually you do it by using the AsDateTime property of the field, so you just use a plain TDateTime Delphi type and let the database access layer deal with the specific database details to store that field.
For example, if your query object is named qTest and the table field is named start_time_stamp, your Delhi variable associated with that persistent field will be named qTeststart_time_stamp, so you can do something like this:
var
StartTS: TDateTime;
begin
qTest.Open;
StartTS := qTeststart_time_stamp.AsDateTime;
ShowMessage('start date is ' + DateTimeToStr(StartTS));
end;
If you use dbExpress and are new to it, read A Guide to Using dbExpress in Delphi database applications
I don't know about MySQL, but if the TField subclass generated is a TSQLTimeStampField, you will need to use the type and functions in the SqlTimSt unit (Data.SqlTimSt for XE2+).
You want to declare the local variables as TSQLTimeStamp
uses Data.SQLTmSt....;
....
var
StartTS: TSQLTimeStamp;
EndTS: TSQLTimeStamp;
begin
StartTS := qTeststart_time_stamp.AsSQLTimeStamp;
SQLTmSt also includes functions to convert to and from TSQLTimeStamp, e.g. SQLTimeStampToDateTime and DateTimeToSQLTimeStamp.
P.S. I tend to agree that using a timestamp as a primary key is likely to cause problems. I would tend to use a auto incrementing surrogate key as Sir Rufo suggests.