Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bi-directional unique key constraint for combination of two columns

Tags:

mysql

mariadb

I have the below table columns in MySQL.

id
user_primary_email
user_secondary_email

I want to make the combination of columns user_primary_email and user_secondary_email unique which I can achieve by using UNIQUE KEY unique_key_name (user_primary_email, user_secondary_email)

The above addition of unique key constraint will help me achieve the below scenario or rather just by adding a unique key to the individual column itself.

user_primary_email = '[email protected]' AND user_secondary_email = '[email protected]'
user_primary_email = '[email protected]' AND user_secondary_email = '[email protected]' //This will not be allowed to enter due to unique key constraint

Now the problem which I am facing is the same combination should not be allowed to add in a reverse way as mentioned below.

user_primary_email = '[email protected]' AND user_secondary_email = '[email protected]' //This should not be allowed to add since already same email id combination added once

id    |   user_primary_email   |   user_secondary_email
-------------------------------------------------------
1     |   [email protected]        |   [email protected]
-------------------------------------------------------
2     |   [email protected]        |   [email protected]       
-------------------------------------------------------

In the above case during insert of row id 2 it should throw error as both the email id combination is already used in row id 1.

Any help would be great.

like image 261
user850234 Avatar asked Dec 08 '16 13:12

user850234


2 Answers

In any MariaDB:

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `user_primary_email` varchar(64) DEFAULT NULL,
  `user_secondary_email` varchar(64) DEFAULT NULL,
  `mycheck` varchar(128) AS (IF(user_primary_email<user_secondary_email,CONCAT(user_primary_email,user_secondary_email),CONCAT(user_secondary_email,user_primary_email))) PERSISTENT,
  PRIMARY KEY (`id`),
  UNIQUE KEY `mycheck` (`mycheck`)
);

MariaDB [test]> insert into t values (1,'a','b',null);
Query OK, 1 row affected (0.03 sec)

MariaDB [test]> insert into t values (2,'b','a',null);
ERROR 1062 (23000): Duplicate entry 'ab' for key 'mycheck'
like image 138
elenst Avatar answered Sep 29 '22 01:09

elenst


There is no direct support for that, but you can use a workaround to create your bidirectional key: You need a unique key on an ordered version of your two columns.

Fortunately, you can very easily do that. MySQL 5.7.6+ supports generated columns and unique indexes for them, which you can use to order your two values and to enforce uniqueness.

create table testBiDirKey (
  a varchar(100), 
  b varchar(100),
  a_ordered varchar(100) as (least(a, b)) STORED,
  b_ordered varchar(100) as (greatest(a, b)) STORED,
  unique key unqBi_test_ab (a_ordered, b_ordered)
);

insert into testBiDirKey(a,b) values('a', 'b');
insert into testBiDirKey(a,b) values('b', 'a');
Error Code: 1062. Duplicate entry 'a-b' for key 'unqBi_test_ab'

This will treat null exactly as your current normal unique key, so

insert into testBiDirKey(a,b) values('a', null);
insert into testBiDirKey(a,b) values('a', null);
insert into testBiDirKey(a,b) values(null, 'a');

are all allowed. You can add coalesce(x,'') to only allow one empty value (either null OR '') if you want. If you verify your values before you add them (e.g. if they don't contain a ,), you can combine the two columns to just one, concatenated with an , - although with little benefit apart from just having 1 additional column.

For 5.7.8+, you don't need the STORED keyword anymore to be able to use these columns in an index. That keyword effects if the values are stored (using disk space) or calculated when required (default).

Before MySQL 5.7.6, you can use a trigger (on update and insert) to update the two columns with the these values, the same logic applies, it's just a little more code.

like image 26
Solarflare Avatar answered Sep 29 '22 03:09

Solarflare