Here is a table
CREATE TABLE `mytable` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`val` char(128) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY (val)
) ENGINE=InnoDB DEFAULT CHARSET=ascii;
Any idea why is this happening, I expected it to set id to zero in the first query itself
MariaDB > insert into mytable set id=0, val="" on duplicate key update id=0, val=val;
Query OK, 1 row affected (0.01 sec)
MariaDB > select * from mytable;
+----+-------+
| id | val |
+----+-------+
| 1 | |
+----+-------+
1 row in set (0.00 sec)
MariaDB > insert into mytable set id=0, val="" on duplicate key update id=0, val=val;
Query OK, 2 rows affected (0.01 sec)
MariaDB > select * from mytable;
+----+-------+
| id | val |
+----+-------+
| 0 | |
+----+-------+
1 row in set (0.00 sec)
MariaDB > insert into mytable set id=0, val="" on duplicate key update id=0, val=val;
Query OK, 0 rows affected (0.01 sec)
Any explanation will be appreciated.
Update: I am aware of using AUTO_INCREMENT=0 but the real question here is that query explicitly set id=0, so why it is setting it as 1 in first query. It seems mysql ok to set it 0 in duplicate instance.
Thanks
When inserting a new record, setting an AUTO_INCREMENT column to 0 means "generate a new value for this column" (ref). Values for AUTO_INCREMENT columns start from 1. Thus:
insert into mytable set id=0, val="" on duplicate key update id=0, val=val;
is equivalent to:
insert into mytable set id=1, val="";
The second insert you call would create a duplicate key (for the val field, not the id field). This causes the update statement to be run, thus updating id to zero. The "2 rows affected" message appears because the on duplicate key update statement returns 2 in case an existing row is updated (ref).
The third insert does nothing. Both keys are duplicate, but the existing row doesn't need to be updated because its values are already what you expect them to be. In this case the on duplicate key update statement returns "0 rows affected".
By default, the starting value for AUTO_INCREMENT is 1, and it will increment by 1 for each new record.
To let the AUTO_INCREMENT sequence start with another value, use the following SQL statement:
ALTER TABLE myTabel AUTO_INCREMENT=0;
Credits
The answer about NO_AUTO_VALUE_ON_ZERO is correct, although a bit incomplete. There is an option to sql_mode to allow for an explicit value of zero to be entered in an autoincrement field. By default 0 is treated the same as null. If you add the NO_AUTO_VALUE_ON_ZERO option, you are allowed to specify a zero value in that field. I have this in my cnf file:
sql_mode='NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
please check NO_AUTO_VALUE_ON_ZERO option.
By default, auto_increment column cannot be inserted zero value.
If you set NO_AUTO_VALUE_ON_ZERO on, you can force to input auto_increment column zero value.
Related
while creating a table I forgot to add a date of birth column. Is there a way to add a NOT NULL column to a table with default value being current date?
You can use alter table. In MySQL 8.0:
alter table mytable
add datetime_of_birth datetime
not null
default (current_date);
In earlier versions, you can't however, have a date column whose value defaults to current_date. This feature is supported for datetimes a and timestamps column only. If you can live with a datetime column, then:
alter table mytable
add datetime_of_birth datetime
not null
default current_timestamp;
You can then ignore the time portion of the column when querying it. If that's not an acceptable solution, then you need to implement the logic with a trigger.
Use alter table:
alter table t add column date_of_birth date;
If your table has rows of data in it, you must either add the column as nullable, or else give it a DEFAULT that will be assigned on every row.
If you don't give a DEFAULT, then a NOT NULL column will attempt to add the "default default." A date's default is '0000-00-00' which is an invalid date, and it will be rejected if you have strict mode set (which I do recommend):
mysql> create table mytable (id int primary key );
Query OK, 0 rows affected (0.02 sec)
mysql> insert into mytable values (1);
Query OK, 1 row affected (0.01 sec)
mysql> alter table mytable add column date_of_birth date not null;
ERROR 1292 (22007): Incorrect date value: '0000-00-00' for column 'date_of_birth' at row 1
The DATETIME or TIMESTAMP data types can be given a default of CURRENT_TIMESTAMP:
mysql> alter table mytable add column date_of_birth datetime not null default current_timestamp;
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
But you said you wanted a column that is DATE, which doesn't allow this:
mysql> alter table mytable add column date_of_birth date not null default current_timestamp;
ERROR 1067 (42000): Invalid default value for 'date_of_birth'
In MySQL 8.0, you can use expressions as defaults (note the extra parentheses around the expression, which are required):
mysql> alter table mytable add column date_of_birth date not null default (curdate());
Query OK, 1 row affected (0.04 sec)
Records: 1 Duplicates: 0 Warnings: 0
If you're using an earlier version of MySQL, then you can't do this in one ALTER TABLE. You must first add the column as a nullable column, then use UPDATE to backfill values into all the existing rows, then ALTER TABLE again to modify the column to be NOT NULL.
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 am trying to figure out if is possible to set an Integer, auto increment as primary key, could start with 0? like 01, 02,0234324 ?
Is it possible??
Should a numeric ID , that is also a primary key have that format? Dont know if possible, but in case it is, would it be a good practice?
Thanks
In MySQL an auto-increment field has to be an integer. For an integer there is no difference between 02 and 2 as far as storage is concerned. However, you can change the default display using the ZEROFILL attribute like this:
> create table t1 (n int(11) zerofill not null auto_increment primary key);
Query OK, 0 rows affected (0.07 sec)
> insert into t1 values (null);
Query OK, 1 row affected (0.02 sec)
> select * from t1;
+-------------+
| n |
+-------------+
| 00000000001 |
+-------------+
1 row in set (0.03 sec)
Suppose i have two below table:
CREATE TABLE post (
id bigint(20) NOT NULL AUTO_INCREMENT,
text text ,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1;
CREATE TABLE post_path (
ancestorid bigint(20) NOT NULL DEFAULT '0',
descendantid bigint(20) NOT NULL DEFAULT '0',
length int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (ancestorid,descendantid),
KEY descendantid (descendantid),
CONSTRAINT f_post_path_ibfk_1
FOREIGN KEY (ancestorid) REFERENCES post (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT f_post_path_ibfk_2
FOREIGN KEY (descendantid) REFERENCES post (id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
And inserted these rows:
INSERT INTO
post (text)
VALUES ('a'); #// inserted row by id=1
INSERT INTO
post_path (ancestorid ,descendantid ,length)
VALUES (1, 1, 0);
When i want to update post row id:
UPDATE post SET id = '10' WHERE post.id =1
MySQL said:
#1452 - Cannot add or update a child row: a foreign key constraint fails (test.post_path, CONSTRAINT f_post_path_ibfk_2 FOREIGN KEY (descendantid) REFERENCES post (id) ON DELETE CASCADE ON UPDATE CASCADE)
Why? what is wrong?
Edit:
When i inserted these rows:
INSERT INTO
post (text)
VALUES ('b'); #// inserted row by id=2
INSERT INTO
post_path (ancestorid, descendantid, length)
VALUES (1, 2, 0);
And updated:
UPDATE post SET id = '20' WHERE post.id =2
Mysql updated successfully both child and parent row.
so Why i can not update first post (id=1)?
Ok, I ran your schema and queries through a test database I have access too and noticed the following; after inserting both rows to both tables, and before any updates the data looks like:
mysql> select * from post;
+----+------+
| id | text |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
mysql> select * from post_path;
+------------+--------------+--------+
| ancestorid | descendantid | length |
+------------+--------------+--------+
| 1 | 1 | 0 |
| 1 | 2 | 0 |
+------------+--------------+--------+
2 rows in set (0.00 sec)
After I issue the update statement, to update post.id to 20:
mysql> UPDATE `post` SET `id` = '20' WHERE `post`.`id` =2;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from post_path;
+------------+--------------+--------+
| ancestorid | descendantid | length |
+------------+--------------+--------+
| 1 | 1 | 0 |
| 1 | 20 | 0 |
+------------+--------------+--------+
2 rows in set (0.00 sec)
Notice that the ancestorid is still 1, this appears to be an issue with MySQL:
If you use a multiple-table UPDATE statement involving InnoDB tables for which there are foreign key constraints, the MySQL optimizer might process tables in an order that differs from that of their parent/child relationship. In this case, the statement fails and rolls back. Instead, update a single table and rely on the ON UPDATE capabilities that InnoDB provides to cause the other tables to be modified accordingly. See Section 14.3.5.4, “InnoDB and FOREIGN KEY Constraints”.
The reason why your first query is failing, is because ancestorid is not updating to 10, but descendantid is, and because you are trying to set post.id to 10, and ancestorid in post_path table is still referencing the value 1, which would no longer exist.
You should consider altering your schema to avoid this, and to also avoid updating an auto_increment column so you avoid collisions.
I believe the solution to your problem is to remove descendantid as a constraint and use a trigger to perform an update on the field.
delimiter $$
CREATE TRIGGER post_trigger
AFTER UPDATE ON post
FOR EACH ROW
BEGIN
UPDATE post_path SET post_path.descendantid = NEW.id WHERE post_path.descendantid = OLD.id
END$$
The main reason why the second one worked is that you have kept different values for ancestorid and descendantid. When you are making two different constraints on the basis of a change on a particular attributes. only the first constraint will work, not the second one. Which is the case in your first update try.
The reason the first update fails and second does not is because in the second instance your ancestorid and descendantid reference different rows in your post table,
ancestorid = 1
descendantid = 2
The first update fails when it attempts to update post_path.ancestorid as in doing so the constraint between post.id and post_path.descendantid fails as these values would no longer match (1 !== 10).
Assuming that any given post cannot be both an ancestor and a descendant then the issue here is only in the execution of the first insert:
INSERT INTO `post_path` (`ancestorid` ,`descendantid` ,`length`) VALUES (1, 1, 0);
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