Составной первичный ключ, содержащий два внешних ключа, ссылающихся на одну и ту же таблицу: SQL Server и MySQL - PullRequest
3 голосов
/ 10 сентября 2010

Я прочитал несколько постов, касающихся дизайна таблицы БД для общего сценария "один ко многим / пользователям для друзей".Один пост включал следующее:

ПОЛЬЗОВАТЕЛИ

* user_id (primary key)
* username

ДРУЗЬЯ

* user_id (primary key, foreign key to USERS(user_id))
* friend_id (primary key, foreign key to USERS(user_id))

> Это остановит дубликаты (IE: 1, 2)не произойдет, но не остановит развороты, потому что (2, 1) действует.Вам понадобится триггер, чтобы убедиться, что есть только один экземпляр отношения ...

Жирная часть побудила меня ответить на вопрос: есть ли разница между тем, как SQL Server иMySQL обрабатывает эти типы составных ключей?Требуется ли обоим этот триггер, о котором упоминает автор, для обеспечения уникальности?

Я спрашиваю, потому что до этого момента я использовал подобную структуру таблиц в SQL Server, без каких-либо таких триггеров.К счастью, я не столкнулся с этой змеей дублирования данных, которая скрывается в траве?

Ответы [ 4 ]

5 голосов
/ 10 сентября 2010

Да, все СУБД будут относиться к этому одинаково.Причина в том, что СУБД предполагает, что столбец имеет значение.Т.е. кортеж не состоит из бессмысленных чисел.Каждый атрибут имеет значение.user_id предполагается, что имеет значение, отличное от friend_id.Таким образом, разработчик обязан создать правило, которое утверждает, что 1,2 эквивалентно 2,1.

2 голосов
/ 10 сентября 2010

Если отношение является симметричным, то одна альтернатива состоит в том, чтобы «определить» отношение как асимметричное в базе данных, но просто всегда добавлять оба кортежа каждый раз, когда вы добавляете один из них.

Вы в основном говорите: «Природа дружбы в БС асимметрична, А может быть другом Б, в то время как Б не дружит с А, но приложение всегда будет добавлять (или удалять) ОБА записи (а, В) и(B, A) каждый раз, когда я добавляю (удаляю) либо. Это также упрощает логику запроса, так как вам больше не нужно просматривать оба столбца. Одна дополнительная вставка / удаление каждый раз, когда вы изменяете данные, но меньшее количество операций чтения при запросах...

2 голосов
/ 10 сентября 2010

Если ваши дружеские отношения симметричны, вам нужно добавить CHECK(user_id < friend_id) в определение таблицы и вставить такие данные:

INSERT
INTO    friends
VALUES  (
        (CASE user_id < friend_id THEN user_id ELSE friend_id END),
        (CASE user_id > friend_id THEN user_id ELSE friend_id END)
        )

В SQL Server вы можете построить индекс UNIQUE для пары вычисляемых столбцов:

CREATE TABLE friends (orestes INT, pylades INT, me AS CASE WHEN orestes < pylades THEN orestes ELSE pylades END, friend AS CASE WHEN orestes > pylades THEN orestes ELSE pylades END)

CREATE UNIQUE INDEX ux_friends_me_friend ON friends (me, friend)

INSERT
INTO    friends
VALUES  (1, 2)

INSERT
INTO    friends
VALUES  (2, 1)
-- Fails

Чтобы получить всех друзей для данного пользователя, вам нужно выполнить этот запрос:

SELECT  friend_id
FROM    friends
WHERE   user_id = @myuser
UNION ALL
SELECT  user_id
FROM    friends
WHERE   friend_id = @myuser

Однако в MySQL может быть эффективнее всегда сохранять каждую из двух копий каждой пары.

Вы можете найти эту статью интересной:

2 голосов
/ 10 сентября 2010

Вы можете просто использовать проверочное ограничение, friend_id > user_id, чтобы предотвратить "развороты". Это привело бы к невозможности ввода пары, такой как (2, 1), такая связь должна быть введена как (1, 2).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...