Duplicate entry '111-222' for key 'PRIMARY' when inserting a new value to MySQL database - mysql

I have a MySQL table running on AWS RDS with structure like the following:
CREATE TABLE `my_table` (
`col1` int(11) NOT NULL,
`col2` int(11) NOT NULL DEFAULT '0',
`f_name` varchar(45) DEFAULT NULL,
`l_name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`col1`,`col2`),
KEY `idx_col1` (`col1`),
KEY `idx_col2` (`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The query
SELECT * FROM my_table WHERE col1=111 AND col2=222;
returns 0 row.
But when I run an insert query
INSERT INTO my_table
(col1, col2, f_name, l_name)
VALUES (111, 222, 'John', 'Doe')
I got an error saying
Duplicate entry '111-222' for key 'PRIMARY'.
Why does this happen? The table doesn't contain a row with col1=111 and col2=222.
There's already a row with values col1=111, col2=111, f_name='John', and l_name='Doe'. But I don't think this would cause a duplicate entry error.
=========================== EDIT ======================================
There's a trigger that generates the duplicate error. Here's the script to reproduce the error.
# Initialize the tables
CREATE TABLE `my_table` (
`col1` int(11) NOT NULL,
`col2` int(11) NOT NULL DEFAULT '0',
`f_name` varchar(45) DEFAULT NULL,
`l_name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`col1`,`col2`),
KEY `idx_col1` (`col1`),
KEY `idx_col2` (`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `triggered_table` (
`col1` int(11) NOT NULL,
`col2` int(11) NOT NULL DEFAULT '0',
`update_date` bigint(20) DEFAULT NULL,
PRIMARY KEY (`col1`,`col2`),
KEY `idx_col1` (`col1`),
KEY `idx_col2` (`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# Insert the data that cause duplicate error
INSERT INTO triggered_table (col1, col2) VALUES (111, 222);
# Create the trigger
DELIMITER $$
CREATE TRIGGER weird_trigger AFTER INSERT
ON my_table
FOR EACH ROW
BEGIN
INSERT INTO triggered_table
(col1, col2)
VALUES (NEW.col1, NEW.col2);
END$$
DELIMITER ;
# Create the duplicate error
INSERT INTO my_table
(col1, col2, f_name, l_name)
VALUES (111, 222, 'John', 'Doe');
I really don't understand why the developers created the triggered_table table. Why didn't they put update_date column to my_table?
This is so weird.

All you have to do is:
Truncate your table then run (Assuming that you have only a test data but if not, you have to do some backup first)
INSERT INTO my_table
(col1, col2, f_name, l_name)
VALUES (111, 222, 'John', 'Doe')
Now if the error still exists, this is a pretty much problem.
Your error seems like you concatinated col1 and col2 as your primary key ('111-222')
You can try
select * from yourTable where FieldPrimary = '111-222' if it is already exists

The duplicate key error does not come from the my_table table but from the triggered_table table instead. When you add a row in triggered_table for the key (111, 222) and then add a new row in the my_table table (with the same key), your trigger will also try to add a new row with the key (111, 222) in your triggered_table. However there is already such a key in use and you will get the duplicate key error.
Depending on what you want to do with the my_table and triggered_table tables, you might want to change the trigger to use REPLACE INTO instead of INSERT INTO. Or you run a check with SELECT first to see if you need to add a new row or not. After that you can run an UPDATE query to change the value of update_date. But to answer your question, the duplicate key error comes from the duplicate key in the triggered_table table.

Related

MySQL getting always error 1451 on update a single value in a table

I need a very little help.... I have two tables A and B where B contains a field that is in constraint key with the ID field of table A. Since I needed to update for a record of A the ID taken from another database, equal to the one I am working on, but with the ID in question "correct", so I did:
dropped the foreign key of table B on that field referenced to the ID of A
updated at all records on the old value of the field in B with the new value of reference ID where that field of B = to the old value
then I update the ID of table A with the new value and I get back error 1451 of foreign key on that field of B that I had already updated without problems even if I had dropped the foreign key in B already;
then I do SET FOREIGN_KEY_CHECKS = 0;
I try to update the ID of table A for that record with the new value but I always get error 1451 .....
since I dropped the foreign key and did the set as well, is there something else or command that I have to consider? thanks everyone in advance to all!
EDIT:
precisely I followed like this scenario:
CREATE TABLE sample_A (
ID bigint(20) UNSIGNED NOT NULL,
product varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE sample_B (
ID bigint(20) UNSIGNED NOT NULL,
ref_id_sample_A_id bigint(20) UNSIGNED NOT NULL,
document_name varchar(300) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE sample_A
ADD PRIMARY KEY ( ID )
ALTER TABLE sample_A
MODIFY ID bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=211388375;
COMMIT;
ALTER TABLE sample_B
ADD PRIMARY KEY ( ID );
ALTER TABLE sample_B
MODIFY ID bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=158;
ALTER TABLE sample_B
ADD CONSTRAINT fk_sample_A_sample_B_ref_id FOREIGN KEY ( ref_id_sample_A_id ) REFERENCES sample_A ( ID );
COMMIT;
INSERT INTO sample_A (ID, product) VALUES
(1, 'product 1'),
(211388370, 'product 2'),
(211388371, 'product 3'),
(211388372, 'product 4'),
(211388373, 'product 5');
INSERT INTO sample_B (ID, ref_id_sample_A_id, document_name) VALUES
(1, 211388372, 'document 1'),
(2, 211388371, 'document 2'),
(3, 1, 'document 3'),
(4, 211388373, 'document 4'),
(5, 1, 'document 5'),
(6, 211388370, 'document 6'),
(7, 211388371, 'document 7');
via phpMyAdmin , dropped foreign key fk_sample_A_sample_B_ref_id
UPDATE sample_B SET ref_id_sample_A_id = 211388369 WHERE ref_id_sample_A_id = 1; <---- OK
UPDATE sample_A SET ID = 211388369 WHERE ID = 1; <---- error 1451
SET FOREIGN_KEY_CHECKS = 0;
UPDATE sample_A SET ID = 211388369 WHERE ID = 1; <---- error 1451 again
next operation would be ALTER TABLE sample_B ADD CONSTRAINT fk_sample_A_sample_B_ref_id FOREIGN KEY ( ref_id_sample_A_id ) REFERENCES sample_A ( ID ); to restore foreign key
Nevermind... I solved... the problem was phpMyAdmin who had the option "check for foreigns enabled"... thanks anyway #Akina!

How to get auto-increment PK on a multi-row insert in MySql

I need to get back a list of "affected" ids when inserting multiple rows at once. Some rows might already be there, resulting in an update instead.
DDL:
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`email` varchar(100) NOT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`update_time` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
UNIQUE KEY `email` (`email`)
)
Query:
INSERT INTO users (id, email, is_active)
VALUES (NULL, "joe#mail.org", true),
(NULL, "jack#mail.org", false),
(NULL, "dave#mail.org", true)
ON DUPLICATE KEY UPDATE
is_active = VALUES(is_active)
There is a UNIQUE constraint on email.
From what I gathered, LAST_INSERT_ID() would only give me first generated id of the batch. But I wouldn't know how many inserts/updates really took place.
The only way I could come up with is to follow with a second SELECT statement:
SELECT id
FROM users
WHERE email IN ("joe#mail.org", "jack#mail.org", "dave#mail.org")
Is there a better way?

Ignore cascade on foreign key update?

To preface, I'm not very experienced with database design. I have a table of hashes and ids. When a group of new hashes are added, each row in the group gets the same id. If any hash within the new group already exists in the database, all hashes in the new group and existing group(s) get a new, shared id (effectively merging ids when hashes are repeated):
INSERT INTO hashes
(id, hash)
VALUES
($new_id, ...), ($new_id, ...)
ON DUPLICATE KEY UPDATE
repeat_count = repeat_count + 1;
INSERT INTO hashes_lookup SELECT DISTINCT id FROM hashes WHERE hash IN (...);
UPDATE hashes JOIN hashes_lookup USING (id) SET id = '$new_id';
TRUNCATE TABLE hashes_lookup;
Other tables reference these ids, so that if an id changes, foreign key constraints take care of updating the ids across tables. The issue here, however, is that I can't enforce uniqueness across any of the child tables. If I do, my queries fail with:
Foreign key constraint for table '...', record '...' would lead to a duplicate entry in table '...'
This error makes sense, given the following test case where id and value are a composite unique key:
id | value
---+-------
a | 1
b | 2
c | 1
Then a gets changed to c:
id | value
---+-------
c | 1
b | 2
c | 1
But c,1 already exists.
It would be ideal if there was an ON UPDATE IGNORE CASCADE option, so that if a duplicate row exists, any duplicating inserts are ignored. However, I'm pretty sure the real issue here is my database design, so I am open to any and all suggestions. My current solution is to not enforce uniqueness across child tables, which leads to a lot of redundant rows.
Edit:
CREATE TABLE `hashes` (
`hash` char(64) NOT NULL,
`id` varchar(128) NOT NULL,
`repeat_count` int(11) NOT NULL DEFAULT '0',
`insert_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `hash` (`hash`) USING BTREE,
KEY `id` (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `emails` (
`id` varchar(128) NOT NULL,
`group_id` char(5) NOT NULL,
`email` varchar(500) NOT NULL,
KEY `index` (`id`) USING BTREE,
UNIQUE KEY `id` (`id`,`group_id`,`email`(255)) USING BTREE,
CONSTRAINT `emails_ibfk_1` FOREIGN KEY (`id`) REFERENCES `hashes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
I think will be good to create table hash_group to store id of hash group:
CREATE TABLE `hash_group` (
`id` BIGINT AUTO_INCREMENT NOT NULL,
`group_name` varchar(128) NOT NULL,
`insert_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `group_name` (`group_name`) USING BTREE,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
And change structure of existing tables:
CREATE TABLE `hashes` (
`hash` char(64) NOT NULL,
`hash_group_id` BIGINT NOT NULL,
`repeat_count` int(11) NOT NULL DEFAULT '0',
`insert_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `hash` (`hash`) USING BTREE,
KEY `hashes_hash_group_id_index` (`hash_group_id`) USING BTREE,
CONSTRAINT `hashes_hash_group_id_fk` FOREIGN KEY (`hash_group_id`) REFERENCES `hash_group` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `emails` (
`hash_group_id` BIGINT NOT NULL,
`group_id` char(5) NOT NULL,
`email` varchar(500) NOT NULL,
KEY `emails_hash_group_id_index` (`hash_group_id`) USING BTREE,
UNIQUE KEY `emails_unique` (`hash_group_id`,`group_id`,`email`(255)) USING BTREE,
CONSTRAINT `emails_ibfk_1` FOREIGN KEY (`hash_group_id`) REFERENCES `hash_group` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Also create trigger to update hash group if you need to do it:
DELIMITER $$
CREATE TRIGGER `update_hash_group_name` AFTER UPDATE ON `hashes`
FOR EACH ROW
BEGIN
UPDATE `hash_group`
SET `group_name` = md5(now()) -- replace to you hash formula
WHERE id = NEW.hash_group_id;
END;$$
DELIMITER ;
And create function for getting actual group id:
DROP FUNCTION IF EXISTS get_hash_group;
DELIMITER $$
CREATE FUNCTION get_hash_group(id INT) RETURNS INT
BEGIN
IF (id IS NULL) THEN
INSERT INTO `hash_group` (`group_name`)
VALUES (md5(now())); -- replace to you hash
RETURN LAST_INSERT_ID();
END IF;
RETURN id;
END;$$
DELIMITER ;
Scenario:
Initial fill:
INSERT INTO `hash_group` (id, group_name) VALUES
(1, 'test1'),
(2, 'test2'),
(3, 'test3');
INSERT INTO `hashes` (hash, hash_group_id) VALUES
('hash11', 1),
('hash12', 1),
('hash13', 1),
('hash2', 2),
('hash3', 3);
INSERT INTO `emails` (hash_group_id, group_id, email)
VALUES
(1, 'g1', 'example1#'),
(2, 'g1', 'example2#'),
(3, 'g1', 'example2#');
Updating of hash_group scenario:
START TRANSACTION;
-- Get #min_group_id - minimum group id (we will leave this id and delete other)
SELECT MIN(hash_group_id) INTO #min_group_id
FROM hashes
WHERE hash IN ('hash11', 'hash12', 'hash2', 'hash15');
-- Replace other group ids in email table to #min_group_id
UPDATE `emails`
SET `hash_group_id` = #min_group_id
WHERE `hash_group_id` IN (
SELECT hash_group_id
FROM hashes
WHERE #min_group_id IS NOT NULL
AND hash IN ('hash11', 'hash12', 'hash2', 'hash15')
-- Update only if we are gluy several hash_groups
AND `hash_group_id` > #min_group_id
);
-- Delete other hash_groups and leave only group with #min_group_id
DELETE FROM `hash_group` WHERE `id` IN (
SELECT hash_group_id
FROM hashes
WHERE #min_group_id IS NOT NULL
AND hash IN ('hash11', 'hash12', 'hash2', 'hash15')
-- Delete only if we are gluy several hash_groups
AND `hash_group_id` > #min_group_id
);
-- #group_id = existing hash_group.id or create new if #min_group_id is null (all inserted hashes are new)
SELECT get_hash_group(#min_group_id) INTO #group_id;
-- Now we can insert new hashes.
INSERT INTO `hashes` (hash, hash_group_id) VALUES
('hash11', #group_id),
('hash12', #group_id),
('hash2', #group_id),
('hash15', #group_id)
ON DUPLICATE KEY
UPDATE repeat_count = repeat_count + 1;
COMMIT;
I maybe wrong but I think you mis-named the id field in hashes.
I think you should rename the id field in hashes to something like group_id, then have a AUTO_INCREMENT field called id that should also be PRIMARY in hashes that the id in emails refers to this field instead. When you want to update and relate all the hashes together, you update the group_id field instead of id, and id remains unique across the table.
This way you can avoid the cascade problem, also you will always know the original hash that the email was referring to.
Sure, if you want to fetch all the hashes related to an email (old and the new) you must exectue and extra query, but I think it solves all your problems.
Edit:
you can use a trigger to do this
The trigger goes like this
DELIMITER $$
CREATE TRIGGER `update_hash_id` AFTER UPDATE ON `hashes`
FOR EACH ROW
BEGIN
UPDATE `emails` SET `id` = NEW.id WHERE `id` = OLD.id;
END;$$
DELIMITER ;
and you must remove the foreign key relation too.
The solution, which we have arrived in chat chat:
/* Tables */
CREATE TABLE `emails` (
`group_id` bigint(20) NOT NULL,
`email` varchar(500) NOT NULL,
UNIQUE KEY `group_id` (`group_id`,`email`) USING BTREE,
CONSTRAINT `emails_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `entities` (`group_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `hashes` (
`group_id` bigint(20) NOT NULL,
`hash` varchar(128) NOT NULL,
`repeat_count` int(11) NOT NULL DEFAULT '0',
UNIQUE KEY `hash` (`hash`),
KEY `group_id` (`group_id`),
CONSTRAINT `hashes_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `entities` (`group_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `entities` (
`group_id` bigint(20) NOT NULL,
`entity_id` bigint(20) NOT NULL,
PRIMARY KEY (`group_id`),
KEY `entity_id` (`entity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `entity_lookup` (
`entity_id` bigint(20) NOT NULL,
PRIMARY KEY (`entity_id`) USING HASH
) ENGINE=MyISAM DEFAULT CHARSET=latin1
/* Inserting */
START TRANSACTION;
/* Determine next group ID */
SET #next_group_id = (SELECT MAX(group_id) + 1 FROM entities);
/* Determine next entity ID */
SET #next_entity_id = (SELECT MAX(entity_id) + 1 FROM entities);
/* Merge any entity ids */
INSERT IGNORE INTO entity_lookup SELECT entity_id FROM entities JOIN hashes USING(group_id) WHERE HASH IN(...);
UPDATE entities JOIN entity_lookup USING(entity_id) SET entity_id = #next_entity_id;
TRUNCATE TABLE entity_lookup;
/* Add the new group ID to entity_id */
INSERT INTO entities(group_id, entity_id) VALUES(#next_group_id, #next_entity_id);
/* Add new values into hashes */
INSERT INTO hashes (group_id, HASH) VALUES
(#next_group_id, ...)
ON DUPLICATE KEY UPDATE
repeat_count = repeat_count + 1;
/* Add other new values */
INSERT IGNORE INTO emails (group_id, email) VALUES
(#next_group_id, "email1");
COMMIT;
Adding an extra integer column to each of the child tables would avoid this problem altogether by using it as a primary key. The key never changes because it isn't a reference to anything else.
Using composite keys as primary keys is generally something that you want to avoid. And considering that this key combination is not always unique, I would definitely say you need a dedicated primary key in all of your child tables with this problem.
You can even auto increment it so you aren't manually assigning it every time. For example..
Create Table exampleTable
(
trueID int NOT NULL AUTO_INCREMENT,
col1 int NOT NULL,
col2 varChar(50)
PRIMARY KEY(trueID)
)
Then, when two of the rows in a child table are set with identical values (for whatever reason), the primary key stays unique, preventing any conflicts in the Database that could arise.

Mysql BIGINT comparison not evaluating correctly

So I've been having a problem and either I am forgetting something fundamental or I found a bug. I've narrowed it down to this:
My table:
CREATE TABLE `tbl` (
`id` int(11) NOT NULL,
`timestmp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Simple. My query:
insert into tbl (id, timestmp) values (1, 1)
on duplicate key update timestmp = if(values(timestmp) > timestmp,
values(timestmp), timestmp)
Table will contain
(1, 1)
Repeat the query with
values (1, 2)
Table will contain
(1, 2)
Repeat the query with
values (1, 1)
Table will still contain
(1, 2)
Great. Just what we want. Instead with this table:
CREATE TABLE `tbl` (
`id` int(11) NOT NULL,
`timestmp` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Let's do the same queries:
insert into tbl (id, timestmp) values (1, 1)
on duplicate key update timestmp = if(values(timestmp) > timestmp,
values(timestmp), timestmp)
Table contains:
(1, 1)
Query with:
values (1, 2)
Table contains:
(1, 1)
What? Let's try
values (1, -1)
Table contains:
(1, -1)
JKLFSD!!!!! What's going on? Is this supposed to happen? Am I completely misinterpreting something? Do I need a special function for bigint comparison?

INSERT statement for MySQL table

CREATE TABLE IF NOT EXISTS `MyTable` (
`ID` SMALLINT NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO MyTable (ID,Name) VALUES (ID=4,Name='xxx')
or
INSERT INTO MyTable (Name) VALUES (Name='xxx')
The problem is that both INSERT statements produce the entry (4,0). Why 0 instead of "xxx"?
UPDATE: Primary key changed.
This should do the job :
INSERT INTO MyTable (ID, Name) VALUES (4, 'xxx')
I'm pretty sure it would be something like this, instead...
INSERT INTO MyTable (Name) VALUES ('xxx')
No need for the Name= part, since you've already specified which column you wish to insert into with the first (Name) definition.
Because the expression Name='xxx' is false, hence evaluates as zero.
You use the column=expression method use in on duplicate key update clauses as described here, not in the "regular" section of inserts. An example of that:
insert into mytable (col1,col2) values (1,2)
on duplicate key update col1 = col1 + 1
You should be using the syntax:
INSERT INTO MyTable (ID,Name) VALUES (4,'xxx')
Is that syntax of Name='xxx' valid? Never seen it before, i assume it is seeing it as an unquoted literal, trying to convert it to a number and coming up with 0? I'm not sure at all
Try this:
INSERT INTO MyTable (Name) VALUES ('xxx')
This is because you should mention the name of the column in the values part. And also because you do not define you primary key correctly (airlineID is not part of the field list)
CREATE TABLE IF NOT EXISTS `MyTable` (
`ID` SMALLINT NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO MyTable (ID,Name) VALUES (4,'xxx')
INSERT INTO MyTable (Name) VALUES ('xxx')
Try this
INSERT INTO MyTable (ID,Name) VALUES (4,xxx)
For more Info just visit this link