Unexpected behaviour INT NOT NULL DEFAULT -1 - mysql

I rarely work with negative numbers (except personal finances) so perhaps that's why there's a gap in my knowledge here...
Consider the following, prompted by a response to a question asked by another user in SO (How to achieve default value if column value is NULL?):
-- Mysql Version 5.5.16
-- sql_mode = ''
DROP TABLE prices;
CREATE TABLE prices (price_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,price INT SIGNED NOT NULL DEFAULT -1);
INSERT INTO prices (price) VALUES (' '),(''),(NULL);
INSERT INTO prices (price_id) VALUES (NULL);
SELECT * FROM prices;
Expected output:
+----------+-------+
| price_id | price |
+----------+-------+
| 1 | -1 |
| 2 | -1 |
| 3 | -1 |
| 4 | -1 |
+----------+-------+
Actual output:
+----------+-------+
| price_id | price |
+----------+-------+
| 1 | 0 |
| 2 | 0 |
| 3 | 0 |
| 4 | -1 |
+----------+-------+
Why?
Intermediate answer: In a nutshell, it seems that if you want to be sure of inserting the default value (when sql_mode is not set), either omit the column from the INSERT or explicitly INSERT a DEFAULT value, i.e.: INSERT INTO prices (price) VALUES(DEFAULT); To me, this goes against the spirit of a DEFAULT value !?!?

Seems like:
a.) If you provide a NULL value to a not null numeric field (not autoincrementing), the default is zero.
b.) If you dont provide a value (as in the last row), you use the given default value (-1)
http://dev.mysql.com/doc/refman/5.0/en/data-type-defaults.html

If you use Mysql STRICT MODE then the result could be different.
Currently you are providing a value NULL, the server tries to map this value to the closest INT value. The server is not using the default value of -1 because it is taking NULL as a valid value.

Related

MySQL - Create table with current date and return date that adjusts [duplicate]

Pretty straight forward question here, I think this should work but it doesn't. Why doesn't it?
CREATE TABLE INVOICE(
INVOICEDATE DATE NOT NULL DEFAULT CURRENT_DATE
)
It doesn't work because it's not supported
The DEFAULT clause specifies a default value for a column. With one exception, the default value must be a constant; it cannot be a function or an expression. This means, for example, that you cannot set the default for a date column to be the value of a function such as NOW() or CURRENT_DATE. The exception is that you can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP column
http://dev.mysql.com/doc/refman/5.5/en/create-table.html
According to this documentation, starting in MySQL 8.0.13, you will be able to specify:
CREATE TABLE INVOICE(
INVOICEDATE DATE DEFAULT (CURRENT_DATE)
)
MySQL 8.0.13 was released to General Availability in October 2018. The release info is located here.
declare your date column as NOT NULL, but without a default. Then add this trigger:
USE `ddb`;
DELIMITER $$
CREATE TRIGGER `default_date` BEFORE INSERT ON `dtable` FOR EACH ROW
if ( isnull(new.query_date) ) then
set new.query_date=curdate();
end if;
$$
delimiter ;
Currently from MySQL 8 you can set the following to a DATE column:
In MySQL Workbench, in the Default field next to the column, write: (curdate())
If you put just curdate() it will fail. You need the extra ( and ) at the beginning and end.
create table the_easy_way(
capture_ts DATETIME DEFAULT CURRENT_TIMESTAMP,
capture_dt DATE AS (DATE(capture_ts))
)
(MySQL 5.7)
I have the current latest version of MySQL: 8.0.20
So my table name is visit, my column name is curdate.
alter table visit modify curdate date not null default (current_date);
This writes the default date value with no timestamp.
----- 2016-07-04 MariaDB 10.2.1 -- Release Note -- -----
Support for DEFAULT with expressions (MDEV-10134).
----- 2018-10-22 8.0.13 General Availability -- -- -----
MySQL now supports use of expressions as default values in data type specifications. This includes the use of expressions as default values for the BLOB, TEXT, GEOMETRY, and JSON data types, which previously could not be assigned default values at all. For details, see Data Type Default Values.
As the other answer correctly notes, you cannot use dynamic functions as a default value. You could use TIMESTAMP with the CURRENT_TIMESTAMP attribute, but this is not always possible, for example if you want to keep both a creation and updated timestamp, and you'd need the only allowed TIMESTAMP column for the second.
In this case, use a trigger instead.
I came to this page with the same question in mind, but it worked for me!, Just thought to update here , may be helpful for someone later!!
MariaDB [niffdb]> desc invoice;
+---------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+----------------+
| inv_id | int(4) | NO | PRI | NULL | auto_increment |
| cust_id | int(4) | NO | MUL | NULL | |
| inv_dt | date | NO | | NULL | |
| smen_id | int(4) | NO | MUL | NULL | |
+---------+--------+------+-----+---------+----------------+
4 rows in set (0.003 sec)
MariaDB [niffdb]> ALTER TABLE invoice MODIFY inv_dt DATE NOT NULL DEFAULT (CURRENT_DATE);
Query OK, 0 rows affected (0.003 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [niffdb]> desc invoice;
+---------+--------+------+-----+-----------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+-----------+----------------+
| inv_id | int(4) | NO | PRI | NULL | auto_increment |
| cust_id | int(4) | NO | MUL | NULL | |
| inv_dt | date | NO | | curdate() | |
| smen_id | int(4) | NO | MUL | NULL | |
+---------+--------+------+-----+-----------+----------------+
4 rows in set (0.002 sec)
MariaDB [niffdb]> SELECT VERSION();
+---------------------------+
| VERSION() |
+---------------------------+
| 10.3.18-MariaDB-0+deb10u1 |
+---------------------------+
1 row in set (0.010 sec)
MariaDB [niffdb]>
While creating a table, you have to use CURRENT_DATE() function as default value. Please see below example I just tested.
CREATE TABLE SALES_DATA (
SALES_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
SALES_GIRL_ID INT UNSIGNED NOT NULL,
SALES_DATE DATE NOT NULL DEFAULT (CURRENT_DATE()),
TOTAL_SALES FLOAT(6, 2),
PRIMARY KEY (SALES_ID),
FOREIGN KEY (SALES_GIRL_ID) REFERENCES SALES_GIRLS(ID)
);

Laravel Schema: Require at least one of four columns is not null

I have a structure that looks like this:
Attribute Values Table
+----+-----------+---------------+-------------+-------------+
| id | option_id | integer_value | price_value | text_value |
+----+-----------+---------------+-------------+-------------+
| 1 | 4 | NULL | NULL | NULL |
| 2 | NULL | 24 | NULL | NULL |
| 3 | NULL | NULL | NULL | Lorem Ipsum |
| 4 | NULL | NULL | 30.50 | NULL |
+----+-----------+---------------+-------------+-------------+
(Some columns were removed for brevity)
But basically the type of the entry can either be a reference to a select option (option_id), or an integer value (integer_value), or a decimal value (price_value), or a text value (text_value). One of the omitted columns is attribute_id which corresponds to an entry that has an enum which stores which one of these four columns is it.
Is there any way to add a constraint in the Laravel schema that at least one of these four columns has to be non-null?
If you will always have only one of these column filled at any time I would recomend altering your schema to just two colums
Like
+----------+--------------+------------+
| id | value | type |
+----------+--------------+------------+
Where the value will always have a value and type will contain the 4 types that you have mentioned
ie: option_id, integer_value, price_value and text_value
This way its clean.
In laravel you cannot add the specified constraint. You must validate your input before insertion.
However if you really want to add a database level constraint you could try adding a trigger before each insert to validate your need using the DB::unprepared function on the table.
and the trigger could look like this (I have not tried this)
DB::unprepared("
DELIMITER $$
CREATE TRIGGER `foo`
BEFORE INSERT ON `table`
FOR EACH ROW
BEGIN
IF (
new.option_id IS NULL
AND new.integer_value IS NULL
AND new.price_value IS NULL
AND new.text_value IS NULL
)
THEN SIGNAL SQLSTATE '02000'
SET MESSAGE_TEXT = 'your message here';
END IF;
END$$
DELIMITER ;
");

Alter MySQL's SHOW COLUMS "Extra" value

Is there a way to change te value of the Extra column that is shown with the SHOW COLUMNS/DESCRIBE sentences?
The documentation about this column states the following:
Extra
Any additional information that is available about a given column. The
value is nonempty in these cases:
auto_increment for columns that have the AUTO_INCREMENT attribute.
on update CURRENT_TIMESTAMP for TIMESTAMP or DATETIME columns that
have the ON UPDATE CURRENT_TIMESTAMP attribute.
VIRTUAL GENERATED or VIRTUAL STORED for generated columns.
DEFAULT_GENERATED for columns that have an expression default value.
I have the next table columns information but I wish to remove the Extra value of the start_date column.
Is there a way to do this?
+--------------------+--------------------+------+-----+-------------------+-------------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------------+------+-----+-------------------+-------------------+
| id_machine_product | "int(10) unsigned" | NO | PRI | NULL | auto_increment |
| ... | ... | ... | ... | ... | ... |
| start_date | timestamp | NO | | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
+--------------------+--------------------+------+-----+-------------------+-------------------+
EDIT:
I have implemented a fingerprint validation method in PHP that diffs the DESCRIBE tables values, I have database versions in production that doesn't have that Extra value even though those columns have an expression default value, so currently, I wish to alter that value so I don't get errors from my implemented fingerprint validation method in my development environment.
The production databases are in Mysql < 8.0 so, as per Bill Karwin's answer, I'm having trouble with my MySQL development environment version that is 8.0
It's not clear from your question why you want to eliminate the Extra information. It's just noting that the column's default is an expression.
To make the Extra field blank, you must make the column's default either a constant value or NULL.
mysql> create table foo ( id int unsigned primary key, start_date timestamp not null default current_timestamp);
mysql> show columns from foo;
+------------+------------------+------+-----+-------------------+-------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+-------------------+-------------------+
| id | int(10) unsigned | NO | PRI | NULL | |
| start_date | timestamp | NO | | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
+------------+------------------+------+-----+-------------------+-------------------+
mysql> alter table foo modify start_date timestamp default null;
mysql> show columns from foo;
+------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| start_date | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+-------+
Note that the Extra information "DEFAULT_GENERATED" is only present in MySQL 8.0. I suspect it's related to the new feature to support expressions in the DEFAULT clause. Any other expression also results in this Extra information.
mysql > alter table foo modify start_date timestamp default (now() + interval 1 hour);
mysql> show columns from foo;
+------------+------------------+------+-----+---------------------------+-------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------------------------+-------------------+
| id | int(10) unsigned | NO | PRI | NULL | |
| start_date | timestamp | YES | | (now() + interval 1 hour) | DEFAULT_GENERATED |
+------------+------------------+------+-----+---------------------------+-------------------+
Topicstarters comment
I have implemented a fingerprint validation method in PHP that diffs
the DESCRIBE tables values, I have database versions in production
that doesn't have that Extra value even though those columns have an
expression default value, so currently, I wish to alter that value so
I don't get errors from my implemented fingerprint validation method
in my development environment.
The more standard SQL method would be which also works in MySQL 8
Query
SELECT
information_schema.COLUMNS.COLUMN_NAME AS 'Field'
, information_schema.COLUMNS.COLUMN_TYPE AS 'Type'
, information_schema.COLUMNS.IS_NULLABLE AS 'Null'
, information_schema.COLUMNS.COLUMN_KEY AS 'Key'
, information_schema.COLUMNS.COLUMN_DEFAULT AS 'Default'
, information_schema.COLUMNS.EXTRA AS 'Extra'
FROM
information_schema.TABLES
INNER JOIN
information_schema.COLUMNS ON information_schema.TABLES.TABLE_NAME = information_schema.COLUMNS.TABLE_NAME
WHERE
information_schema.TABLES.TABLE_NAME = '<table>'
This query should match the output of DESCRIBE
Then you could use REPLACE() on information_schema.COLUMNS.EXTRA output to remove or edit the way you want. For example removing extra features like DEFAULT_GENERATED or VIRTUAL GENERATED (generated columns)
you need an alter table statement. Something like
ALTER TABLE `document` MODIFY COLUMN `start_date ` INT AUTO_INCREMENT;
You can set a default value like
DEFAULT 1 NOT NULL

CURRENT_DATE/CURDATE() not working as default DATE value

Pretty straight forward question here, I think this should work but it doesn't. Why doesn't it?
CREATE TABLE INVOICE(
INVOICEDATE DATE NOT NULL DEFAULT CURRENT_DATE
)
It doesn't work because it's not supported
The DEFAULT clause specifies a default value for a column. With one exception, the default value must be a constant; it cannot be a function or an expression. This means, for example, that you cannot set the default for a date column to be the value of a function such as NOW() or CURRENT_DATE. The exception is that you can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP column
http://dev.mysql.com/doc/refman/5.5/en/create-table.html
According to this documentation, starting in MySQL 8.0.13, you will be able to specify:
CREATE TABLE INVOICE(
INVOICEDATE DATE DEFAULT (CURRENT_DATE)
)
MySQL 8.0.13 was released to General Availability in October 2018. The release info is located here.
declare your date column as NOT NULL, but without a default. Then add this trigger:
USE `ddb`;
DELIMITER $$
CREATE TRIGGER `default_date` BEFORE INSERT ON `dtable` FOR EACH ROW
if ( isnull(new.query_date) ) then
set new.query_date=curdate();
end if;
$$
delimiter ;
Currently from MySQL 8 you can set the following to a DATE column:
In MySQL Workbench, in the Default field next to the column, write: (curdate())
If you put just curdate() it will fail. You need the extra ( and ) at the beginning and end.
create table the_easy_way(
capture_ts DATETIME DEFAULT CURRENT_TIMESTAMP,
capture_dt DATE AS (DATE(capture_ts))
)
(MySQL 5.7)
I have the current latest version of MySQL: 8.0.20
So my table name is visit, my column name is curdate.
alter table visit modify curdate date not null default (current_date);
This writes the default date value with no timestamp.
----- 2016-07-04 MariaDB 10.2.1 -- Release Note -- -----
Support for DEFAULT with expressions (MDEV-10134).
----- 2018-10-22 8.0.13 General Availability -- -- -----
MySQL now supports use of expressions as default values in data type specifications. This includes the use of expressions as default values for the BLOB, TEXT, GEOMETRY, and JSON data types, which previously could not be assigned default values at all. For details, see Data Type Default Values.
As the other answer correctly notes, you cannot use dynamic functions as a default value. You could use TIMESTAMP with the CURRENT_TIMESTAMP attribute, but this is not always possible, for example if you want to keep both a creation and updated timestamp, and you'd need the only allowed TIMESTAMP column for the second.
In this case, use a trigger instead.
I came to this page with the same question in mind, but it worked for me!, Just thought to update here , may be helpful for someone later!!
MariaDB [niffdb]> desc invoice;
+---------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+----------------+
| inv_id | int(4) | NO | PRI | NULL | auto_increment |
| cust_id | int(4) | NO | MUL | NULL | |
| inv_dt | date | NO | | NULL | |
| smen_id | int(4) | NO | MUL | NULL | |
+---------+--------+------+-----+---------+----------------+
4 rows in set (0.003 sec)
MariaDB [niffdb]> ALTER TABLE invoice MODIFY inv_dt DATE NOT NULL DEFAULT (CURRENT_DATE);
Query OK, 0 rows affected (0.003 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [niffdb]> desc invoice;
+---------+--------+------+-----+-----------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+-----------+----------------+
| inv_id | int(4) | NO | PRI | NULL | auto_increment |
| cust_id | int(4) | NO | MUL | NULL | |
| inv_dt | date | NO | | curdate() | |
| smen_id | int(4) | NO | MUL | NULL | |
+---------+--------+------+-----+-----------+----------------+
4 rows in set (0.002 sec)
MariaDB [niffdb]> SELECT VERSION();
+---------------------------+
| VERSION() |
+---------------------------+
| 10.3.18-MariaDB-0+deb10u1 |
+---------------------------+
1 row in set (0.010 sec)
MariaDB [niffdb]>
While creating a table, you have to use CURRENT_DATE() function as default value. Please see below example I just tested.
CREATE TABLE SALES_DATA (
SALES_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
SALES_GIRL_ID INT UNSIGNED NOT NULL,
SALES_DATE DATE NOT NULL DEFAULT (CURRENT_DATE()),
TOTAL_SALES FLOAT(6, 2),
PRIMARY KEY (SALES_ID),
FOREIGN KEY (SALES_GIRL_ID) REFERENCES SALES_GIRLS(ID)
);

Drop default constraint

I have a tableA with following output from desc tableA command:
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | NO | | | |
| city | varchar(50) | YES | | NULL | |
| state | char(2) | YES | | NULL | |
| country | varchar(30) | YES | | NULL | |
| notes | longtext | YES | | NULL | |
| type | varchar(50) | NO | | NULL | |
+---------+-------------+------+-----+---------+----------------+
Now there are 3 columns with NOT NULL constraints:
id
name
type
For columns id and type, I need to remove the default constraint. Basically I want Default: None. I do not want to use the workarounds eg . setting default to '' for a varchar.
The difference between NULL, NONE, and '' is made more clear from this discussion Default-values-for-varchar-and-int-mysql-data-types
I tried using the command:
alter table tableA alter column type drop default;
The query runs fine, but no rows are affected. And no change in Default value is shown when I run describe command.
If I set the default value to '' I run into different issue - the database allows the entry of empty string in the db. For me that is equivalent to inserting NULL for a column's value, and I do not want to allow that.
I need some guidance on how to handle Default values in this situation where I cannot allow empty strings as data in the db. I want to mention that I am planning to put validations in the code to check if the incoming data is an empty string or NULL. But just in case that validation is not working etc, I want to make sure the DB can refuse to add such data.
Any help is really appreciated.
If the column can be then null, then either default or null are the same.
So Allowed Null, Default null is effectively irrelevant except when doing say
Insert (name,city,type) Values ('Fred',DEFAULT,'Caucasian')
Null isn't an empty string. Given you are allowing null in the table but interpreting it as empty string in your application, you have an irritating flaw in your design.
If you don't want empty strings in there, normally you'd use a check constraint which as far as I know still isn't implemented in mysql. Apparently this lack is usually solved with an insert trigger.
So you'd check the value in the trigger and then fail the insert for empty strings.
PS it doesn't solve the integrity problem, but if you did want a way to put nulls in when a straing was empty so you would not have to distinguish between empty string and null.
Then have a look at the nullif function.