mysql> create database test;
Query OK, 1 row affected (0.01 sec)
mysql> use test;
Database changed
mysql> create table one (id int not null primary key);
Query OK, 0 rows affected (0.03 sec)
mysql> -- here is the problem
mysql> create table two (oneid int not null references one(id));
Query OK, 0 rows affected (0.02 sec)
mysql> -- here are the first signs of issues!!!!
mysql> show create table two;
+-------+----------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------+
| two | CREATE TABLE `two` (
`oneid` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+----------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> -- here is the issue: an insert with no reference
mysql> insert into two values (-12);
Query OK, 1 row affected (0.01 sec)
mysql> select * from two;
+-------+
| oneid |
+-------+
| -12 |
+-------+
1 row in set (0.00 sec)
mysql> -- if you want to know:
mysql> SHOW Variables WHERE Variable_name='foreign_key_checks';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| foreign_key_checks | ON |
+--------------------+-------+
1 row in set (0.00 sec)
After my research, the only question here is: why MySql do not reject table two creation because of its not valid syntax and instead it silently create the table without the foreign key reference?
Just for completeness here is the correct syntax for MySql.
mysql> create table three(oneid int not null, CONSTRAINT whatEverName FOREIGN KEY (oneid) REFERENCES one(id));
Query OK, 0 rows affected (0.04 sec)
MySQL parses but ignores “inline REFERENCES specifications” (as defined in the SQL standard) where the references are defined as part of the column specification. MySQL accepts REFERENCES clauses only when specified as part of a separate FOREIGN KEY specification.
You can go here and read more yourself...
Related
Even though I thought I understood the logic in foreign keys, I cannot make this small example to work.
In this example, I am inserting values that break the foreign key constrain. I would expect to receive an error -- but I do not.
/* info about my server */
select version();
show variables where variable_name = "foreign_key_checks";
/* example starts here */
create table foo (id int primary key) engine = 'InnoDB';
insert into foo values (1);
create table bar (id int primary key references foo(id)) engine = 'InnoDB';
insert into bar values (2); -- should complain here, but does not
commit;
select * from foo;
select * from bar;
This results in:
MariaDB [test]> select version();
+-----------------+
| version() |
+-----------------+
| 10.3.21-MariaDB |
+-----------------+
1 row in set (0.000 sec)
MariaDB [test]> show variables where variable_name = "foreign_key_checks";
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| foreign_key_checks | ON |
+--------------------+-------+
1 row in set (0.002 sec)
MariaDB [test]> create table foo (id int primary key) engine = 'InnoDB';
Query OK, 0 rows affected (0.358 sec)
MariaDB [test]> insert into foo values (1);
Query OK, 1 row affected (0.040 sec)
MariaDB [test]> create table bar (id int primary key references foo(id)) engine = 'InnoDB';
Query OK, 0 rows affected (0.205 sec)
MariaDB [test]> insert into bar values (2);
Query OK, 1 row affected (0.024 sec)
MariaDB [test]> commit;
Query OK, 0 rows affected (0.000 sec)
MariaDB [test]> select * from foo;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.001 sec)
MariaDB [test]> select * from bar;
+----+
| id |
+----+
| 2 |
+----+
1 row in set (0.000 sec)
So since "It looks like [my] post is mostly code; please add some more details.", let me paraphrase here. In the above,
I checked that my server is set up to enforce foreign key contraints,
I explicitly use the InnoDB engine -- the one actually enforcing foreign key contraints.
Despite that, bar contains an id of 2 that is not present in foo. What am I doing wrong here?
insert into foo values (1);
create table bar (id int not null primary key, id_foo int, foreign key (id_foo) references foo(id)); should works for you.
insert into bar values (2,1);
The problem lies in this tricky behavior that is in fact highlighted in the documentation (emphasis mine):
MariaDB accepts the REFERENCES clause in ALTER TABLE and CREATE TABLE column definitions, but that syntax does nothing. MariaDB simply parses it without returning any error or warning, for compatibility with other DBMS's. Before MariaDB 10.2.1 this was also true for CHECK constraints. Only the syntax for indexes described below creates foreign keys.
In short, one must declare foreign key contraints on separate lines if they are to be enforced (thanks #Honeyboy).
i use mysql 5.7.
if i look only into 'information_schema' database, is there a way to distinguish a column with default NULL and a column without default?
here's my table:
mysql> CREATE TABLE defaults (default_null varchar(100) DEFAULT NULL, no_default varchar(100)) ENGINE=InnoDB;
Query OK, 0 rows affected (0.02 sec)
mysql> ALTER TABLE defaults ALTER COLUMN no_default DROP DEFAULT;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE defaults;
+----------+------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------+------------------------------------------------------------------------------------------------------------------------------------------+
| defaults | CREATE TABLE `defaults` (
`default_null` varchar(100) DEFAULT NULL,
`no_default` varchar(100)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------+------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO defaults SET no_default = 'foo';
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO defaults SET default_null = 'bar';
ERROR 1364 (HY000): Field 'no_default' doesn't have a default value
mysql>
no_default and default_null columns are different, but in information_schema.columns table they are the same:
mysql> SELECT column_name, is_nullable, IFNULL(column_default, 'real NULL') FROM information_schema.columns WHERE table_name = 'defaults';
+--------------+-------------+-------------------------------------+
| column_name | is_nullable | IFNULL(column_default, 'real NULL') |
+--------------+-------------+-------------------------------------+
| default_null | YES | real NULL |
| no_default | YES | real NULL |
+--------------+-------------+-------------------------------------+
2 rows in set (0.01 sec)
mysql>
This behaviour has every Version if strict sql mode is enabled.
If strict SQL mode is enabled, an INSERT statement generates an error if it does not specify an explicit value for every column that has no default value. See Section 5.1.10, “Server SQL Modes”.
You can find it under https://dev.mysql.com/doc/refman/5.7/en/insert.html
I have a table "temp_enum_test1" with data type "enum('IE','IS')". When I try to ALTER the table by changing data type into VARCHAR from 'ENUM', getting duplicate entry error. It is accepting records in ENUM type. Even when I query the table I am getting unique rows. Can anyone please help me here. Below are the schema and my approach.
mysql> CREATE TABLE temp_enum_test1 (
-> r_id int(11) NOT NULL,
-> r_type enum('IE','IS'),
-> UNIQUE KEY uk_temp_enum_test1 (r_id,r_type)
-> );
Query OK, 0 rows affected (0.38 sec)
mysql> insert into temp_enum_test1 values(1,'IE');
Query OK, 1 row affected (0.07 sec)
mysql> insert into temp_enum_test1 values(1,'IS');
Query OK, 1 row affected (0.05 sec)
mysql> select * from temp_enum_test1;
+------+--------+
| r_id | r_type |
+------+--------+
| 1 | IE |
| 1 | IS |
+------+--------+
2 rows in set (0.00 sec)
mysql> alter table temp_enum_test1 change column r_type r_type varchar(30);
ERROR 1062 (23000): Duplicate entry '1-I' for key 'uk_temp_enum_test1'
mysql>
The problem seems to be with the create table query. The table has been created with 'UNIQUE KEY' that is causing the error while altering the table. If you can use
PRIMARY KEY (`r_id`)
instead of ...
UNIQUE KEY uk_temp_enum_test1 (r_id,r_type)
Complete CREATE command as below;
CREATE TABLE IF NOT EXISTS temp_enum_test1 (
`r_id` int(11) NOT NULL AUTO_INCREMENT,
`r_type` enum('IE','IS') NOT NULL,
PRIMARY KEY (`r_id`)
)
I want to implement the NULL check logic in the MYSQL. Here is the code:
mysql> create table temp
(
id int,
des varchar(100),
primary key (id)
);
mysql> SELECT ##tx_isolation;
+-----------------+
| ##tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
mysql> start transcation;
mysql> select * from temp where id=0;
Empty set (0.03 sec)
mysql> insert temp (id,des) values(0,'0');
Query OK, 1 row affected (0.11 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
It seems well.
However, there is the possibility of multiple NULL check transaction at the same time for my situation.
Trans 1: Trans 2:
mysql> start transaction; mysql> start transaction;
mysql> select * from temp where id=0;
Empty set (0.03 sec)
mysql> select * from temp where id=0;
Empty set (0.03 sec)
mysql> insert temp (id,des) values(0,'0');
Query OK, 1 row affected (0.11 sec)
mysql> insert temp (id,des) values(0,'0');
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
--block and waiting for the Trans 1 commit;
ERROR 1062 (23000): Duplicate entry '0' for key 'PRIMARY'
Trans 2 will report the ERROR 1062 when Trans 1 commit. I want to avoid the Error and I think it is a universal phenomenon of NULL check in the application.
How to implement the multiple NULL check transaction in mysql in the correct way? Are there any ways to block each other when using the "select" sql in the multiple transcation?
Thank you.
update 2016.07.12
I just simplify the situation I meet with above. In fact, the table I have is similar to
mysql> create table temp
(
id int NOT NULL AUTO_INCREMENT,
des varchar(100),
unique_id int,
primary key (id),
UNIQUE (unique_id)
);
And my transactions are
Trans 1: Trans 2:
mysql> start transaction; mysql> start transaction;
mysql> select * from temp where unique_id=0;
Empty set (0.06 sec)
mysql> select * from temp where unique_id=0;
Empty set (0.02 sec)
mysql> insert temp(des,unique_id) values('0',0);
Query OK, 1 row affected (0.20 sec)
mysql> insert temp(des,unique_id) values('0',0);
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
--block and waiting for the Trans 1 commit;
ERROR 1062 (23000): Duplicate entry '0' for key 'unique_id'
So the PRIMARY KEY is AUTO_INCREMENT in my real case.
If you insist on inserting the primary key yourself, as opposed to having it autoincrement, then this is exactly the expected and desired behaviour that transactions were designed to create.
If you would like Trans 2 to commit, then you need to either not specify the primary key, or handle the primary key allocation in your application.
See log below. (Snipped just for brevity; unsnipped # http://pastebin.com/k9sCM6Ee)
In short: somehow rows are getting assigned ID 0. When this happens, it blocks inserts, even when those inserts aren't actually conflicting with ID 0 (although that really shouldn't happen in the first place).
Although it is heavily read and very heavily inserted (up to ~300k rows/min), this table is never updated. The only method that inserts is the one that results in the INSERT INTO queries like below. There are no foreign keys or the like.
a) WTF?
b) How do I fix it?
Thanks!
$ mysql --version
mysql Ver 14.14 Distrib 5.1.30, for apple-darwin9.4.0 (i386) using readline 5.1
$ mysql
mysql> SHOW CREATE TABLE visitations \G
*************************** 1. row ***************************
Table: visitations
Create Table: CREATE TABLE `visitations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`scraping_id` int(11) NOT NULL,
`site_id` int(11) NOT NULL,
`visited` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_visitations_on_scraping_id_and_site_id` (`scraping_id`,`site_id`),
KEY `index_visitations_on_site_id` (`site_id`)
) ENGINE=InnoDB AUTO_INCREMENT=23525407 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> show triggers;
Empty set (0.04 sec)
mysql> INSERT INTO `visitations` (`scraping_id`,`site_id`,`visited`) VALUES (647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip];
ERROR 1062 (23000): Duplicate entry '0' for key 'PRIMARY'
mysql> SELECT * FROM `visitations` WHERE (`scraping_id`,`site_id`,`visited`) IN ((647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip]);
Empty set (1 min 27.43 sec)
mysql> select * from visitations where id = 0;
+----+-------------+---------+---------+
| id | scraping_id | site_id | visited |
+----+-------------+---------+---------+
| 0 | 645 | 46177 | 0 |
+----+-------------+---------+---------+
1 row in set (0.00 sec)
mysql> delete from visitations where id < 363;
Query OK, 363 rows affected (0.11 sec)
mysql> select * from visitations where id = 0;
Empty set (0.00 sec)
mysql> INSERT INTO `visitations` (`scraping_id`,`site_id`,`visited`) VALUES (647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip];
Query OK, 500 rows affected (0.23 sec)
Records: 500 Duplicates: 0 Warnings: 0
mysql> select * from visitations where id = 0;
Empty set (0.00 sec)
mysql> INSERT INTO `visitations` (`scraping_id`,`site_id`,`visited`) VALUES (647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip];
ERROR 1062 (23000): Duplicate entry '647-196' for key 'index_visitations_on_scraping_id_and_site_id'
You have probably hit a bug like:
auto increment does not work properly with InnoDB after update
You need to track the change history from the release you are using to identify whether fixed bugs can affect you and whether you should upgrade.
MySQL 5.1 Change History