2013-03-22 60 views
5
CREATE TABLE nodes (
     id INTEGER PRIMARY KEY, 
     name VARCHAR(10) NOT NULL, 
     feat1 CHAR(1), -- e.g., age 
     feat2 CHAR(1) -- e.g., school attended or company 
    ); 

    CREATE TABLE edges (
     a INTEGER NOT NULL REFERENCES nodes(id) ON UPDATE CASCADE ON DELETE CASCADE, 
     b INTEGER NOT NULL REFERENCES nodes(id) ON UPDATE CASCADE ON DELETE CASCADE, 
     PRIMARY KEY (a, b) 
    ); 

    CREATE INDEX a_idx ON edges (a); 
    CREATE INDEX b_idx ON edges (b); 

如果我们想要表示一个无向图,我们需要在对的唯一性上添加CHECK约束。Mysql:如何检查对的唯一性

由于SQL标准不允许CHECK约束中的子查询,因此如何检查对的唯一性?

+0

只是澄清 - 这里的目标是防止(1,2)_和_(2,1)都出现在表格中 - 是吗? – 2013-03-22 13:12:53

+0

是的,这是正确的。也不应该有任何自我循环。 – 2013-03-22 13:16:55

回答

3

MySQL不支持CHECK约束。

您可以创建一个BEFORE INSERT和BEFORE UPDATE触发器来检查这种情况,并在需要时抛出一个错误。

实施例:

CREATE TABLE edges(
    a INT(11) NOT NULL, 
    b INT(11) NOT NULL 
); 

DELIMITER $$ 

CREATE TRIGGER trigger1 
BEFORE INSERT 
ON edges 
FOR EACH ROW 
BEGIN 
    SET @cnt = NULL; 

    SELECT COUNT(*) INTO @cnt FROM edges 
    WHERE a = new.a AND b = new.b OR a = new.b AND b = new.a; 

    IF @cnt > 0 THEN 
    SIGNAL SQLSTATE '02000' SET MESSAGE_TEXT = 'Error: uniqueness of pair'; 
    END IF; 
END 
$$ 

DELIMITER ; 

此外,创建类似BEFORE UPDATE触发,以避免对更新NEW错误的值,或只使用一个存储的过程,因为代码是相同的。

2

CHECK不被MySQL支持CREATE TABLE,为documentation决定

的CHECK子句是解析但忽略所有的存储引擎

事实上,有关于这一问题的open bug report因为2004(!)。

我会采取的方法是在插入和更新时创建一个存储过程触发器,如果​​该对存在,故意失败。

7

你可以搭起见状要么(A,B)(B,A)失败触发:

这里是触发:

DELIMITER $$ 
CREATE TRIGGER edges_bi BEFORE INSERT 
ON edges FOR EACH ROW 
BEGIN 
    DECLARE found_count,dummy,diff,SomethingsWrong INT DEFAULT 0; 
    DECLARE errmsg VARCHAR(128); 
    SET diff = new.a - new.b; 
    IF diff = 0 THEN 
     SET errmsg = CONCAT('[',new.a,',',new.b,'] is Vertex, Not Edge'); 
     SET SomethingsWrong = 1; 
    END IF; 
    SELECT COUNT(1) INTO found_count FROM edges 
    WHERE (a=NEW.a AND b=NEW.b) OR (a=NEW.b AND b=NEW.a); 
    IF found_count = 1 THEN 
     SET errmsg = CONCAT('[',new.a,',',new.b,'] Already Exists'); 
     SET SomethingsWrong = 1; 
    END IF; 
    IF SomethingsWrong = 1 THEN 
     SELECT errmsg INTO dummy FROM edges WHERE 1=1; 
    END IF; 
END; $$ 
DELIMITER ; 

下面是一个示例表:

DROP DATABASE if exists saurabh; 
CREATE DATABASE saurabh; 
USE saurabh 
CREATE TABLE edges 
(
    a INTEGER NOT NULL, 
    b INTEGER NOT NULL, 
    PRIMARY KEY (a,b), 
    UNIQUE KEY (b,a) 
); 

注意我有一个PRIMARY KEY和一个UNIQUE KEY,PRIMARY KEY的列反转

让我们创建表:

mysql> DROP DATABASE if exists saurabh; 
Query OK, 1 row affected (0.01 sec) 

mysql> CREATE DATABASE saurabh; 
Query OK, 1 row affected (0.00 sec) 

mysql> USE saurabh 
Database changed 
mysql> CREATE TABLE edges 
    -> (
    -> a INTEGER NOT NULL, 
    -> b INTEGER NOT NULL, 
    -> PRIMARY KEY (a,b), 
    -> UNIQUE KEY (b,a) 
    ->); 
Query OK, 0 rows affected (0.12 sec) 

mysql> 

让我们创建触发器:

mysql> DELIMITER $$ 
mysql> CREATE TRIGGER edges_bi BEFORE INSERT 
    -> ON edges FOR EACH ROW 
    -> BEGIN 
    ->  DECLARE found_count,dummy,diff,SomethingsWrong INT DEFAULT 0; 
    ->  DECLARE errmsg VARCHAR(128); 
    ->  SET diff = new.a - new.b; 
    ->  IF diff = 0 THEN 
    ->   SET errmsg = CONCAT('[',new.a,',',new.b,'] is Vertex, Not Edge'); 
    ->   SET SomethingsWrong = 1; 
    ->  END IF; 
    ->  SELECT COUNT(1) INTO found_count FROM edges 
    ->  WHERE (a=NEW.a AND b=NEW.b) OR (a=NEW.b AND b=NEW.a); 
    ->  IF found_count = 1 THEN 
    ->   SET errmsg = CONCAT('[',new.a,',',new.b,'] Already Exists'); 
    ->   SET SomethingsWrong = 1; 
    ->  END IF; 
    ->  IF SomethingsWrong = 1 THEN 
    ->   SELECT errmsg INTO dummy FROM edges WHERE 1=1; 
    ->  END IF; 
    -> END; $$ 
Query OK, 0 rows affected (0.11 sec) 

mysql> DELIMITER ; 

下面是一些样本数据:

INSERT INTO edges (a,b) VALUES (5,3); 
INSERT INTO edges (a,b) VALUES (3,3); 
INSERT INTO edges (a,b) VALUES (3,5); 
INSERT INTO edges (a,b) VALUES (5,5); 
SELECT * FROM edges; 

让我们尝试加载这些进入edges表:

mysql> INSERT INTO edges (a,b) VALUES (5,3); 
Query OK, 1 row affected (0.00 sec) 

mysql> INSERT INTO edges (a,b) VALUES (3,3); 
ERROR 1366 (HY000): Incorrect integer value: '[3,3] is Vertex, Not Edge' for column 'dummy' at row 1 
mysql> INSERT INTO edges (a,b) VALUES (3,5); 
ERROR 1366 (HY000): Incorrect integer value: '[3,5] Already Exists' for column 'dummy' at row 1 
mysql> INSERT INTO edges (a,b) VALUES (5,5); 
ERROR 1366 (HY000): Incorrect integer value: '[5,5] is Vertex, Not Edge' for column 'dummy' at row 1 
mysql> SELECT * FROM edges; 
+---+---+ 
| a | b | 
+---+---+ 
| 5 | 3 | 
+---+---+ 
1 row in set (0.00 sec) 

注意,阻挡A = B条件防止任何自循环

CAVEAT

如果

  • 你开始与一个空表这个触发不起作用
  • 进入(3,3)作为第一行

因为BEFORE INSERT触发器不在空表上触发。

一旦您输入有效行A <>B然后所有检查都正确执行。

试试吧!

+0

我得到赏金吗? – RolandoMySQLDBA 2013-04-02 12:17:20

0

我想一个答案可能取决于你如何填充edges表,这是不清楚你的问题。如果从nodes表中填充它,则可以基于排除镜像对(即1,2和2,1)的SELECT查询来构建VIEW。这也可能解决级联和删除要求。