两列的两侧 UNIQUE INDEX

Goo*_*bot 6 mysql primary-key duplication unique-constraint

创建具有复合PRIMARY KEYUNIQUE INDEX用于两列的表可保证 col1、col2 的唯一性。是否有一种棘手的方法可以使两列的相反顺序也成为 UNIQUE (col2, col1)?

例如

PRIMARY KEY (col1, col2)
Run Code Online (Sandbox Code Playgroud)

或者

UNIQUE INDEX (col1, col2)
Run Code Online (Sandbox Code Playgroud)

如果我们有col1=33 && col2=54; 我们如何才能避免INSERTcol1=54 && col2=33

Mik*_*ll' 5

在标准的 SQL dbms 中,您可以通过对 id 编号进行排序并使用 CHECK 约束来强制执行这种要求。应用程序代码、存储过程或用户定义的函数负责按正确的顺序放置 ID 号。

create table friends (
  user_a integer not null,  -- references users, not shown
  user_b integer not null,  -- references users, not shown
  primary key (user_a, user_b),
  check (user_a < user_b)
);
Run Code Online (Sandbox Code Playgroud)

但是 MySQL 不强制执行 CHECK 约束。使用 MySQL,我仍然会使用存储过程(而不是应用程序代码)来按正确的顺序放置 ID 号。但我也会不时运行查询或报告,以确保 user_a 的所有值都小于 user_b 的值。

select *
from friends
where user_a >= user_b;
Run Code Online (Sandbox Code Playgroud)

一开始我可能不会包含 UPDATE 语句来修复这些值。我想跟踪哪些进程、应用程序或用户正在绕过我的存储过程以将数据直接插入表中。(您可以撤销对表的直接访问,并要求所有访问都通过存储过程。但这在某些遗留系统中不切实际;需要重写的代码太多。还有其他选择。)

不幸的是,某些要求必须作为管理程序来实施,例如运行报告。


Rol*_*DBA 2

您可能想尝试创建一个触发器来检查 (col1,col2) 是否存在为 (col2,col1)

这是一个例子:

use test
drop table if exists ali;
create table ali
(
    col1 int not null,
    col2 int not null,
    primary key (col1,col2)
);
DELIMITER $$
CREATE TRIGGER ali_bi BEFORE INSERT ON ali FOR EACH ROW 
BEGIN 
    DECLARE found_count,newcol1,newcol2,dummy INT;
    SET newcol1 = NEW.col1;
    SET newcol2 = NEW.col2;
    SELECT COUNT(1) INTO found_count FROM ali
    WHERE col1 = newcol2 AND col2 = newcol1;
    IF found_count = 1 THEN
        SELECT 1 INTO dummy FROM information_schema.tables;
    END IF;
END; $$ 
DELIMITER ;
INSERT INTO ali VALUES (1,2);
INSERT INTO ali VALUES (3,4);
INSERT INTO ali VALUES (2,1);
INSERT INTO ali VALUES (4,3);
SELECT * FROM ali;
Run Code Online (Sandbox Code Playgroud)

该触发器设计为在 FOUND_COUNT 为 1 时故意中断。

这是执行的示例:

mysql> use test
Database changed
mysql> drop table if exists ali;
Query OK, 0 rows affected (0.03 sec)

mysql> create table ali
    -> (
    ->     col1 int not null,
    ->     col2 int not null,
    ->     primary key (col1,col2)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> DELIMITER $$
mysql> CREATE TRIGGER ali_bi BEFORE INSERT ON ali FOR EACH ROW
    -> BEGIN
    ->     DECLARE found_count,newcol1,newcol2,dummy INT;
    ->     SET newcol1 = NEW.col1;
    ->     SET newcol2 = NEW.col2;
    ->     SELECT COUNT(1) INTO found_count FROM ali
    ->     WHERE col1 = newcol2 AND col2 = newcol1;
    ->     IF found_count = 1 THEN
    ->         SELECT 1 INTO dummy FROM information_schema.tables;
    ->     END IF;
    -> END; $$
Query OK, 0 rows affected (0.07 sec)

mysql> DELIMITER ;
mysql> INSERT INTO ali VALUES (1,2);
Query OK, 1 row affected (0.07 sec)

mysql> INSERT INTO ali VALUES (3,4);
Query OK, 1 row affected (0.06 sec)

mysql> INSERT INTO ali VALUES (2,1);
ERROR 1172 (42000): Result consisted of more than one row
mysql> INSERT INTO ali VALUES (4,3);
ERROR 1172 (42000): Result consisted of more than one row
mysql> SELECT * FROM ali;
+------+------+
| col1 | col2 |
+------+------+
|    1 |    2 |
|    3 |    4 |
+------+------+
2 rows in set (0.00 sec)

mysql>
Run Code Online (Sandbox Code Playgroud)

试一试 !!!

注意这不适用于批量插入。仅当一次插入一行时。

我已经使用了这种技术,并在其他 DBA StackExchange 问题中建议了它

更新 2012-02-29 11:46 美国东部时间

我尝试再次插入相同的四行。

INSERT INTO ali VALUES (1,2);
INSERT INTO ali VALUES (3,4);
INSERT INTO ali VALUES (2,1);
INSERT INTO ali VALUES (4,3);
SELECT * FROM ali;
Run Code Online (Sandbox Code Playgroud)

这是我得到的

mysql> INSERT INTO ali VALUES (1,2);
ERROR 1062 (23000): Duplicate entry '1-2' for key 'PRIMARY'
mysql> INSERT INTO ali VALUES (3,4);
ERROR 1062 (23000): Duplicate entry '3-4' for key 'PRIMARY'
mysql> INSERT INTO ali VALUES (2,1);
ERROR 1172 (42000): Result consisted of more than one row
mysql> INSERT INTO ali VALUES (4,3);
ERROR 1172 (42000): Result consisted of more than one row
mysql> SELECT * FROM ali;
+------+------+
| col1 | col2 |
+------+------+
|    1 |    2 |
|    3 |    4 |
+------+------+
2 rows in set (0.00 sec)

mysql>
Run Code Online (Sandbox Code Playgroud)

尽管消息很奇怪,但这种触发方法效果很好。

  • 解决问题的方法非常有趣!虽然我一般不喜欢触发器;但有时它们似乎是必要的;) (2认同)