Законный? Два внешних ключа, ссылающиеся на тот же первичный ключ

Так же, как обновление для Утесов, Спасибо ChaosPandion для шаблона.

Человек
Интервал PersonID PK

Сеть
Интервал PersonID PK FK
Интервал OtherPersonID PK FK

ИЛИ

Человек
Интервал PersonID PK

Сеть
Интервал PersonID PK FK
Интервал FriendID PK FK

Друг
Интервал FriendID PK
Интервал OtherPersonID FK

++++++ Исходное сообщение ниже ++++++

Привет все,

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

В основном мы работаем над сайтом, который реализует "друзей" сеть. Все пользователи сайта будут содержаться в таблице tblUsers с (идентификационные данные интервала PersonID PK, и т.д.).

То, что я желаю сделать, должно составить вторую таблицу, tblNetwork, который будет содержать все отношения между пользователями, с (идентификационные данные интервала NetworkID PK, интервал Owners_PersonID FK, интервал Friends_PersonID FK, и т.д.). Или с другой стороны, удалите NetworkID и имейте и Owners_PersonID и Friends_PersonID, совместно использованный как Первичный ключ.

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

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

То, что DBA желает сделать, вместо того, чтобы хранить и Owners_PersonId и Friends_PersonId в той же таблице, должен составить третью таблицу tblFriends для хранения Friends_PersonId, и иметь tblNetwork имеют (идентификационные данные интервала NetworkID PK, интервал Owner_PersonID FK, интервал FriendsID FK (от TBLFriends)). Все, что содержал бы tblFriends, будет (идентификационные данные интервала FriendsID PK, Friends_PersonID (связано назад с Людьми)).

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

Я понимаю, что технически, то, что он желает, возможно, но это встроено с лучшей практикой? Какова была бы лучшая практика?

Спасибо за чтение цените комментарии.

Ryan

5
задан devRyan 20 April 2010 в 00:42
поделиться

5 ответов

Ваш проект нарушает Третью нормальную форму , если Network.Owners_PersonID хранится в сети с избыточностью.

Но я не понимаю, как на самом деле помогает дизайн DBA. Я ожидал, что Друзья будут таблицей «многие-ко-многим» между пользователями и Сетями :

CREATE TABLE tblUsers (
  PersonID INT IDENTITY PRIMARY KEY
);

CREATE TABLE tblNetworks (
  NetworkID INT IDENTITY PRIMARY KEY,
  Owner_PersonID INT NOT NULL REFERENCES tblUsers
);

CREATE TABLE tblFriends (
  NetworkID INT NOT NULL REFERENCES tblNetworks,
  FriendID INT NOT NULL REFERENCES tblUsers,
  PRIMARY KEY(NetworkID, FriendID)
);

Другими словами, у вас есть простая таблица «многие-ко-многим». отношение ко многим:

Users ----<- Friends ->---- Networks

И, кроме того, Networks ссылается на Users только для идентификации владельца данной сети. Таким образом, для данной сети есть только одна строка, поэтому вы не можете создать аномалию обновления, изменив владельца сети в некоторых строках.

Я не думаю, что это чрезмерное разбиение сущностей на отдельные таблицы. Вы по-прежнему можете получить список друзей для данной сети:

SELECT ... FROM Networks n JOIN Friends f ON (n.NetworkID=f.NetworkID)

Таким способом можно получить всех друзей пользователя из всех сетей (передайте идентификатор данного пользователя в параметре ? ):

SELECT ... FROM Friends u 
JOIN Friends f ON (u.NetworkID=f.NetworkID)
WHERE u.UserID = ?

В вашем первоначальном дизайне это почти то же самое:

SELECT ... FROM Networks u
JOIN Networks f ON (u.Owner_UserID=f.Owner_UserID)
WHERE u.FriendID = ?

Но преимущество в том, что вы устранили возможную аномалию обновления.

2
ответ дан 18 December 2019 в 14:43
поделиться

Я хочу создать вторую таблицу, tblNetwork, которая будет содержать все отношения между пользователями с (NetworkID int identity {{ 1}} PK, Owners_PersonID int FK, Friends_PersonID int FK и т. Д.). Или, наоборот, удалите NetworkID и укажите как Owners_PersonID, так и Friends_PersonID в качестве основного ключа .

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

1
ответ дан 18 December 2019 в 14:43
поделиться

Я говорю, делайте это по-своему. Наличие этой третьей таблицы усложняет программирование.

0
ответ дан 18 December 2019 в 14:43
поделиться

Если я вас правильно понимаю, вы предлагаете:

Person              PersonID PK
FriendList          FriendListID, OwnerID, PersonID 

DBA предлагает:

Person              PersonID PK
FriendList          FriendListID, OwnerID
FriendListEntry     FriendListID, PersonID

Ваш подход потребует нескольких строк для каждый друг в списке. Это будет повторять OwnerID несколько раз, нарушая нормальную форму. Решение DBA более нормализовано, имея только значения, зависящие от FriendListID в таблице FriendList.

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

7
ответ дан 18 December 2019 в 14:43
поделиться

Единственная схема, которая имеет для меня смысл, такова:

Person
    PersonID Int PK

Friend
    PersonID Int PK FK
    OtherPersonID Int PK FK

Итак, у вас может быть процедура под названием FriendList , которая выполняет этот красивый чистый запрос:

Select Person.*
From Friend
    Inner Join Person On Friend.OtherPersonID = Person.PersonID
Where Friend.PersonID = @PersonID;

Я не одобряю выбор всех столбцов.

3
ответ дан 18 December 2019 в 14:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: