Многие ко многим с «Первичным» - PullRequest
6 голосов
/ 02 октября 2008

Я работаю над базой данных, которая должна представлять компьютеры и их пользователей. Каждый компьютер может иметь несколько пользователей, и каждый пользователь может быть связан с несколькими компьютерами, так что это классическое отношение «многие ко многим». Однако также должна существовать концепция «основного» пользователя. Я должен иметь возможность присоединиться к основному пользователю, чтобы получить список всех компьютеров с их основными пользователями. Я не уверен, что лучше всего структурировать это в базе данных:

1) Как я сейчас делаю: связывание таблицы с логическим столбцом IsPrimary. Для присоединения требуется что-то вроде ON (c.computer_id = l.computer_id AND l.is_primary = 1). Это работает, но кажется неправильным, потому что нелегко ограничить данные только одним основным пользователем на компьютер.

2) Поле в таблице компьютера, которое указывает непосредственно на строку пользователя, все строки в таблице пользователя представляют неосновных пользователей. Это лучше отражает ограничение на один первичный компьютер, но усложняет получение списка пользователей компьютеров.

3) Поле в таблице компьютеров, связывающее строку в таблице связей. Чувствует себя странно ...

4) Что-то еще?

Что такое «реляционный» способ описания этих отношений?

EDIT: @Mark Brackett: Третий вариант кажется мне гораздо менее странным, потому что вы показали, как хорошо он выглядит. По какой-то причине я даже не думал об использовании составного внешнего ключа, поэтому я подумал, что мне нужно добавить столбец идентификаторов в таблицу ссылок, чтобы он работал. Выглядит отлично, спасибо!

@ j04t: Круто, я рад, что мы сейчас согласны с №3.

Ответы [ 7 ]

7 голосов
/ 02 октября 2008

Вариант 3, хотя он может показаться странным, ближе всего к тому, что вы хотите моделировать. Вы бы сделали что-то вроде:

User { 
   UserId 
   PRIMARY KEY (UserId) 
}

Computer { 
   ComputerId, PrimaryUserId
   PRIMARY KEY (UserId) 
   FOREIGN KEY (ComputerId, PrimaryUserId) 
      REFERENCES Computer_User (ComputerId, UserId) 
}

Computer_User { 
   ComputerId, UserId 
   PRIMARY KEY (ComputerId, UserId)
   FOREIGN KEY (ComputerId) 
      REFERENCES Computer (ComputerId)
   FOREIGN KEY (UserId) 
      REFERENCES User (UserId)
}

Что дает вам 0 или 1 основного пользователя (PrimaryUserId может быть обнуляем, если хотите), который должен быть в Computer_User. Редактировать: если пользователь может быть основным только для 1 компьютера, то UNIQUE CONSTRAINT на Computer.PrimaryUserId будет применять это. Обратите внимание, что не требуется, чтобы все пользователи были основными на каком-либо компьютере (это было бы отношением 1: 1 и требовало бы, чтобы они были в одной таблице).

Редактировать: некоторые запросы, чтобы показать вам простоту этого дизайна

--All users of a computer
SELECT User.* 
FROM User 
JOIN Computer_User ON 
   User.UserId = Computer_User.UserId 
WHERE 
   Computer_User.ComputerId = @computerId

--Primary user of a computer
SELECT User.* 
FROM User 
JOIN Computer ON 
   User.UserId = Computer.PrimaryUserId
WHERE 
   Computer.ComputerId = @computerId

--All computers a user has access to
SELECT Computer.*
FROM Computer
JOIN Computer_User ON
   Computer.ComputerId = Computer_User.ComputerId
WHERE
   Computer_User.UserId = @userId

--Primary computer for a user
SELECT Computer.*
FROM Computer
WHERE
    PrimaryUserId = @userId
1 голос
/ 02 октября 2008

Редактировать - Я не думал об этом первые 3 раза ... Я голосую за - (Решение № 3)

Пользователи

user id (pk)

Компьютеры

computer id (pk)
primary user id (fk -> computer users id)

Пользователи компьютеров

user id (pk) (fk -> user id)
computer id (pk) (fk -> user id)

Это лучшее решение, которое я могу придумать.

Почему мне нравится этот дизайн.

1) Поскольку это отношения с участием компьютеров и пользователей, мне нравится идея связать пользователя с несколькими компьютерами в качестве основного пользователя. Это может никогда не произойти, когда эта база данных используется.

2) Причина, по которой мне не нравится наличие primary_user в таблице ссылок

 (computer_users.primary_user_id fk-> users.user_id)

- чтобы компьютер никогда не имел нескольких основных пользователей.

Учитывая эти причины, решение номер 3 выглядит лучше, поскольку вы никогда не столкнетесь с некоторыми возможными проблемами, которые я вижу с другими подходами.

Решение 1 проблемы - возможно иметь несколько основных пользователей на компьютер.

Проблема решения 2. Компьютер связывается с основным пользователем, когда компьютер и пользователь не связаны друг с другом.

computer.primaryUser = user.user_id
computer_users.user_id != user.user_id

Решение 3 проблемы - это кажется странным, не так ли? Кроме этого я не могу думать ни о чем.

Решение 4 проблемы - я не могу придумать другой способ сделать это.


Это четвертое редактирование, поэтому я надеюсь, что оно все еще имеет смысл.

0 голосов
/ 02 октября 2008

У нас похожая ситуация в приложении, над которым я работаю, когда у нас есть учетные записи, к которым может быть подключено много клиентов, но только один должен быть основным клиентом.

Мы используем таблицу ссылок (как у вас), но в таблице ссылок есть значение Sequence. Основной пользователь - это тот, у кого Sequence = 1. Затем, у нас есть индекс в этой таблице ссылок для AccountID и Sequence, чтобы гарантировать уникальность комбинации AccountID и Sequence (таким образом, гарантируя, что два клиента не смогут быть основным в учетной записи). Таким образом, вы должны иметь:

LEFT JOIN c.computer_id = l.computer_id AND l.sequence = 1
0 голосов
/ 02 октября 2008

2 мне кажется правильным, но я бы проверил 1, 2 и 3 на производительность по типам запросов, которые вы обычно выполняете, и по типам объемов данных, которые у вас есть.

Как общее практическое правило, я склонен полагать, что там, где есть выбор реализаций, вы должны учитывать свои требования к запросам и разрабатывать свою схему, чтобы добиться максимальной производительности и использования ресурсов в наиболее распространенном случае.

В редкой ситуации, когда у вас есть одинаково распространенные случаи, которые предлагают противоположные реализации, используйте бритву Оккама.

0 голосов
/ 02 октября 2008

Любое решение 1 или 2 будет работать. В этот момент я бы спросил себя, с кем будет легче работать. Я использовал оба метода в разных ситуациях, хотя в общем случае я использовал флаг в таблице ссылок, а затем устанавливал уникальные ограничения для computer_id и isPrimaryUser, таким образом вы гарантировали, что на каждом компьютере будет только один основной пользователь.

0 голосов
/ 02 октября 2008

Я бы создал еще одну таблицу PRIMARY_USERS с уникальным значением на computer_id и сделал бы внешние ключи computer_id и user_id для ПОЛЬЗОВАТЕЛЕЙ.

0 голосов
/ 02 октября 2008

Поскольку основной пользователь - это функция компьютера, и пользователь, я бы предпочел придерживаться вашего подхода, чтобы основной пользователь был столбцом в таблице ссылок.

Другая альтернатива, о которой я могу подумать, - это иметь столбец primaryUser непосредственно на самой таблице компьютера.

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