2013-03-22 65 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。這也可能解決級聯和刪除要求。