简体   繁体   中英

MariaDB 10.0, innodb: Conditional Unique Constraint?

My schema, simplified:

people: id (int), name (string), allow_duplicate_name (int)

allow_duplicate_name is 1 for true, 0 for false.

I want to prevent new records from being inserted if their name fields are non-unique, but only if they have allow_duplicate_name == 1. So if we have the following table:

id  name  allow_duplicate_name
1   jack  1
2   jack  1
3   ryan  1
4   jack  1

Then the following statement should succeed:

INSERT INTO people (name, allow_duplicate_name)
VALUES ('jack', 1);

But this statement should fail:

INSERT INTO people (name, allow_duplicate_name)
VALUES ('jack', 0);

My root problem is that I have a race condition where two concurrent transactions both check if a duplicate exists, see that it doesn't, and then insert a new record with the same name and allow_duplicate_name=0.

Another idea I had is to use READ UNCOMMITTED when checking to see if duplicates had been added before committing each transaction, and roll it back instead, letting the application take a different course of action (eg, pick a new name), since we can tolerate (it would be much less than ideal, but we can handle it...) the race condition where each transaction sees the other and then rolls itself back, but we absolutely cannot allow an insert/update that sets a duplicate name with allow_duplicate_name=0.

I've heard that our setup is such that stored procedures are massive pain to work with, but maybe a stored procedure is the only sane way to solve this?

You can almost do what you want. but you'll want to change the name of the variable. The key idea is that unique indexes allow NULL values to be duplicated. So, when "allow_duplicate_name" is NULL , then you can get duplicates.

I would suggest renaming the field to something like DuplicatesNotAllowed . Then create the index:

 create unique index idx_people_name_duplicatesnotallowed on people(name, duplciatesnotallowed);

You can insert the values as:

INSERT INTO people (name, duplciatesnotallowed)
    VALUES ('jack', NULL);

This will succeed.

INSERT INTO people (name, allow_duplicate_name)
    VALUES ('jack', 1);

This will fail the second time it is run. But, if you update the field for the existing rows, then this will fail. I'm not sure if that meets your needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM