2

これは、2 つの列に対する一般的な制約ではありません。

以下は、外部キー ref1、ref2 を持つテーブルです。

connection_id | ref1_id  | ref2_id
1             |     1    |    2

許可したい:

connection_id | ref1_id  | ref2_id
1             |     1    |    2
2             |     1    |    3

ただし、次の 2 つを許可しません。

(typical unique key on ref1,ref2 - this I know how to do)
connection_id | ref1_id  | ref2_id
1             |     1    |    2
2             |     1    |    2

だけでなく!!:

(this is the problem)
connection_id | ref1_id  | ref2_id
1             |     1    |    2
2             |     2    |    1

ref1-ref2 のペアが 1 つだけ必要なため、(ref1,ref2) または (1,2) のペアは (2,1) と同じであり、一意のキー制約によって許可されない必要があります。MySQLでそれを行う方法はありますか?

すでに回答されていると確信していますが、検索すると、常に2つの列で典型的な一意の制約にヒットします。

4

3 に答える 3

1
DROP TABLE pair CASCADE;
CREATE TABLE pair
      ( pair_id SERIAL NOT NULL PRIMARY KEY
      , aaa INTEGER NOT NULL
      , bbb INTEGER NOT NULL
      , CONSTRAINT aaa_bbb UNIQUE (aaa, bbb)
    );

CREATE UNIQUE INDEX aaaXXXbbb ON pair ( LEAST(aaa, bbb), GREATEST(aaa, bbb) )
        ;
INSERT INTO pair(aaa,bbb) VALUES(1,1), (1,2),(2,2);

INSERT INTO pair(aaa,bbb) VALUES(2,1);

SELECT * FROM pair;

結果:

INSERT 0 3
ERROR:  duplicate key value violates unique constraint "aaaxxxbbb"
DETAIL:  Key ((LEAST(aaa, bbb)), (GREATEST(aaa, bbb)))=(1, 2) already exists.
 pair_id | aaa | bbb 
---------+-----+-----
       1 |   1 |   1
       2 |   1 |   2
       3 |   2 |   2
(3 rows)

mysql が式に対する制約またはインデックスを許可および強制するかどうかはわかりません。Postgres は式のインデックスを許可しますが、残念ながら式に対する制約はありません。インデックスが不可能な場合aaa >= bbb、@vearutob の回答のように、明らかなタイブレーク制約はもちろん になります。

ところで: このモデルでは、(aaa,bbb) の制約は冗長です。これは、条件が既にインデックスによってカバーされているためです。(私はそれらを交換したいと思います: {least,greatest} の制約と裸の列のインデックスですが、postgres はまだこれを許可していないようです)

于 2012-09-19T11:01:39.977 に答える
1

INSERT/クエリでデータの正規の順序を使用するUPDATE(たとえば、ref1_id は常に ref2_id より小さい) か、ON BEFORE INSERT/UPDATEトリガーでカスタム チェックの重複を使用する必要があります。

このタスクは、拘束だけでは解決できません。

編集:トリガーを使用して中止INSERTまたはUPDATEステートメントする方法がないため、ソリューション全体がさらに悪化します:-)

DELIMITER ###

CREATE TRIGGER `after_up` 
  AFTER UPDATE ON `my_table`
FOR EACH ROW 
BEGIN
  DECLARE collision INT DEFAULT 0;
  SELECT 1  
    INTO collision
    FROM my_table 
    WHERE ref1_id = NEW.ref2_id AND ref2_id = NEW.ref1_id;
  IF collision
  THEN -- reverting update
    UPDATE my_table SET ref2_id = OLD.ref2_id, ref1_id = OLD.ref1_id WHERE connection_id = OLD.connection_id;
  END IF;
END

###

CREATE TRIGGER `after_in` 
  AFTER INSERT ON `my_table`
FOR EACH ROW 
BEGIN
  DECLARE collision INT DEFAULT 0;
  SELECT 1  
    INTO collision
    FROM my_table 
    WHERE ref1_id = NEW.ref2_id AND ref2_id = NEW.ref1_id;
  IF collision
  THEN -- deleting new row
    DELETE FROM my_table WHERE connection_id = NEW.connection_id;
  END IF;
END

###

delimiter ;

編集 2: トリガーでクエリを中止するハックが見つかりました ( DROP TABLE nonexistent_table_name)

DELIMITER ###

CREATE TRIGGER `before_up` 
  BEFORE UPDATE ON `my_table`
FOR EACH ROW 
BEGIN
  DECLARE collision INT DEFAULT 0;
  SELECT 1  
    INTO collision
    FROM my_table 
    WHERE ref1_id = NEW.ref2_id AND ref2_id = NEW.ref1_id;
  IF collision
  THEN -- throwing error
     DROP TABLE __error_duplicate_detected;        
  END IF;
END

###

CREATE TRIGGER `before_in` 
  BEFORE INSERT ON `my_table`
FOR EACH ROW 
BEGIN
  DECLARE collision INT DEFAULT 0;
  SELECT 1  
    INTO collision
    FROM my_table 
    WHERE ref1_id = NEW.ref2_id AND ref2_id = NEW.ref1_id;
  IF collision
  THEN -- throwing error
     DROP TABLE __error_duplicate_detected;        
  END IF;
END

###

delimiter ;
于 2012-09-19T10:08:08.933 に答える
1

バリアント 1: ref1_id および ref2_id のデータ型が整数データ型の制限内にありsizeof ref1_id + sizeof ref2_id、mysql に適合する整数データ型が存在することを考慮します。

新しい計算列をテーブルに追加し、 min(ref1_id, ref2_id) << (size_in_bits_of_the_biggest_of_the_types_of_ref1_id_and_ref2_id) + max(ref1_id, ref2_id)挿入または更新の前にトリガー内で計算できる型で計算します。この方法では、一意の制約違反のために行全体が拒否されます。

バリアント 2: バリアント 1 が適用できない場合は、速度が低下するため、これを使用します。

新しい計算列をテーブルに varchar として追加して、テキスト表現に適合するのに十分な長さconcat(max(ref1_id), ' ', max(ref2_id))- 偽のコード、ここで最大可能値を使用し、 CONCAT(min(ref1_id, ref2_id), ' ', max(ref1_id, ref2_id))挿入または更新の前にトリガー内で計算します - この方法では、一意のために行全体が拒否されます制約違反。

于 2012-09-19T15:00:07.263 に答える