Как лучше всего обеспечить уникальность ФК во всех перестановках? - PullRequest
0 голосов
/ 24 февраля 2019

У меня есть отношение Friendship, которое отображает два пользователя вместе.

Вот два ФК:

  1. fk_friend_one_id
  2. fk_friend_two_id

У меня есть UNIQUE INDEX для (fk_friend_one_id, fk_friend_two_id), но вы можете вставить идентификаторы обоими способами, и все равно будет работать:

INSERT INTO friendship (fk_friend_one_id, fk_friend_two_id) VALUES (1, 2);
INSERT INTO friendship (fk_friend_one_id, fk_friend_two_id) VALUES (2, 1);

Как мне обеспечить уникальность в этом случае?

Вот некоторые решения, о которых я подумал:

  1. Просто всегда вставляйте в обоих порядке.
  2. Убедитесь, что fk_friend_two_id> fk_friend_one_id.К сожалению, это не может быть применено на уровне базы данных с MySQL, потому что CHECK ничего не делает.

Ответы [ 3 ]

0 голосов
/ 24 февраля 2019

Начиная с MySql 8.0.16, вы можете добавить ограничение CHECK , которое действительно что-то делает.

(Между MySql 5.7 и 8.0.16 вы можете добавить один, но он будет просто проигнорирован)

Для MariaDb рабочее ограничение CHECK было реализовано начиная с версии 10.2.1. Ссылка

Так что, если ваша версия поддерживает это, то это может быть что-то вроде этого примера:

create table Friend (
  id int primary key auto_increment, 
  name varchar(30) not null
);

insert into Friend (name) values
('John Doe'), ('Jane Shepard')

create table Friendship (
  id int primary key auto_increment,
  fk_friend_one_id int not null,
  fk_friend_two_id int not null,
  CONSTRAINT chk_friend2_gt_friend1 check (fk_friend_two_id > fk_friend_one_id),
  CONSTRAINT fk_friend_one_id foreign key (fk_friend_one_id) references Friend(id),
  CONSTRAINT fk_friend_two_id foreign key (fk_friend_two_id) references Friend(id),
  CONSTRAINT idx_friends unique index (fk_friend_one_id, fk_friend_two_id)
);
insert into Friendship (fk_friend_one_id, fk_friend_two_id) value (2, 1);
CONSTRAINT `chk_friend2_gt_friend1` failed for `mydb`.`Friendship`
insert into Friendship (fk_friend_one_id, fk_friend_two_id) value (1, 1);
CONSTRAINT `chk_friend2_gt_friend1` failed for `mydb`.`Friendship`
insert into Friendship (fk_friend_one_id, fk_friend_two_id) value (1, 2);
select * from Friendship order by id;
id | fk_friend_one_id | fk_friend_two_id
-: | ---------------: | ---------------:
 1 |                1 |                2

db <> скрипка здесь

0 голосов
/ 06 марта 2019

(Другие Ответы работают слишком усердно. Все, что нужно, это UNIQUE и некоторый простой код SQL.)

Если у вас есть значения $ x и $ y для вставки в таблицу, тогдаОбязательно вводите их в каноническом порядке:

INSERT INTO tbl (a, b)
    VALUES ( LEAST($x, $y),
             GREATEST($x, $y) )

И имеют

UNIQUE(a, b)   -- or, better yet, PRIMARY KEY(a,b)

Обратите внимание, что значения (1,2) или (2,1), единственноеБудет вставлена ​​строка (1,2)

Что нужно сделать, если вы попытаетесь вставить оба?Один войдет как (1,2);другой получит ошибку на INSERT.Вы можете игнорировать ошибку с помощью INSERT IGNORE.Если есть другие столбцы, требующие настройки, рассмотрите возможность использования INSERT .. ON DUPLICATE KEY UPDATE ..

0 голосов
/ 24 февраля 2019

Это может быть достигнуто с помощью хранимых процедур и триггеров .Вы можете попробовать что-то вроде этого:

DELIMITER $

CREATE PROCEDURE `friendship_check`(IN fk_friend_one_id INT, IN fk_friend_two_id INT)
BEGIN
    IF fk_friend_one_id < fk_friend_two_id THEN
        SIGNAL SQLSTATE '45000'
           SET MESSAGE_TEXT = 'check constraint on friendship failed';
    END IF;
END$
DELIMITER ;


-- before insert trigger
DELIMITER $
CREATE TRIGGER `friendship_before_insert` BEFORE INSERT ON `Friendship`
FOR EACH ROW
BEGIN
    CALL friendship_check(new.fk_friend_one_id,new.fk_friend_two_id);
END$
DELIMITER ;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...