Каков наилучший способ обработки отношений один-к-одному в SQL? - PullRequest
11 голосов
/ 11 сентября 2008

Допустим, у меня есть альфа-вещи, которые могут или не могут быть или быть , связанными с Браво или Чарли.

Это отношения один-к-одному: ни одна Альфа не будет относиться к более чем одному Браво. И никакой Браво не будет относиться к более чем одной Альфе.

У меня есть несколько целей:

  • система, которая проста в освоении и поддерживать.
  • целостность данных обеспечивается в моем базы данных.
  • схема, которая соответствует в реальном мире, логическая организация мои данные.
  • классы / объекты в моем программирование этой карты хорошо таблицы базы данных (по типу Linq to SQL)
  • быстрые операции чтения и записи
  • эффективное использование пробела (несколько пустых полей)

У меня есть три идеи & hellip;

PK = primary key  
FK = foreign key  
NU = nullable

Одна таблица с множеством нулевых полей (плоский файл) & hellip;

      Alphas
      --------
   PK AlphaId
      AlphaOne
      AlphaTwo
      AlphaThree
   NU BravoOne
   NU BravoTwo
   NU BravoThree
   NU CharlieOne
   NU CharlieTwo
   NU CharlieThree

Множество таблиц с нулевыми нулевыми полями & hellip;

      Alphas
      --------
   PK AlphaId
      AlphaOne
      AlphaTwo
      AlphaThree

      Bravos
      --------
FK PK AlphaId
      BravoOne
      BravoTwo
      BravoThree

      Charlies
      --------
FK PK AlphaId
      CharlieOne
      CharlieTwo
      CharlieThree

Лучший (или худший) из обоих: множество нулевых внешних ключей для многих таблиц & hellip;

      Alphas
      --------
   PK AlphaId
      AlphaOne
      AlphaTwo
      AlphaThree
NU FK BravoId
NU FK CharlieId

      Bravos
      --------
   PK BravoId
      BravoOne
      BravoTwo
      BravoThree

      Charlies
      --------
   PK CharlieId
      CharlieOne
      CharlieTwo
      CharlieThree

Что если Альфа должна быть Браво или Чарли, но не оба?

Что если вместо бравосов и чарли Альфами могут быть любые дельты, эхо, фокстроты или гольфы и т. Д. & Hellip;?


РЕДАКТИРОВАТЬ: Это часть вопроса: Какая схема базы данных лучше всего подходит для моей навигации?

Ответы [ 9 ]

8 голосов
/ 11 сентября 2008

Если вы хотите, чтобы каждая Альфа была связана только с одним Браво, я бы проголосовал за возможность с использованием комбинированных FK / PK:

      Bravos
      --------
FK PK AlphaId
      BravoOne
      BravoTwo
      BravoThree

Таким образом, один и только один Браво может относиться к вашим Альфам.

Если Браво и Чарли должны быть взаимоисключающими, возможно, самый простой способ - создать поле дискриминатора:

      Alpha
      --------
   PK AlphaId
   PK AlphaType NOT NULL IN ("Bravo", "Charlie")
      AlphaOne
      AlphaTwo
      AlphaThree

      Bravos
      --------
FK PK AlphaId
FK PK AlphaType == "Bravo"
      BravoOne
      BravoTwo
      BravoThree

      Charlies
      --------
FK PK AlphaId
FK PK AlphaType == "Charlie"
      CharlieOne
      CharlieTwo
      CharlieThree

Таким образом, поле AlphaType заставляет записи всегда принадлежать ровно одному подтипу.

4 голосов
/ 22 сентября 2008

Я предполагаю, что вы будете использовать SQL Server 2000/2005. У меня есть стандартный шаблон для отношений 1: 1, который я использую, который не слишком отличается от вашей второй идеи, но вот различия:

  • Каждый объект должен сначала иметь свой собственный первичный ключ, поэтому в ваших таблицах Браво, Чарли и т. Д. Должен быть указан их собственный суррогатный ключ, в дополнение к столбцу внешнего ключа для таблицы Альфа. Вы делаете свою модель домена довольно негибкой, указав, что первичный ключ одной таблицы должен быть точно таким же, как первичный ключ другой таблицы. Следовательно, сущности становятся очень тесно связанными, и одна сущность не может существовать без другой, что не является бизнес-правилом, которое необходимо применять при проектировании базы данных.

  • Добавьте ограничение внешнего ключа между столбцами AlphaID в таблицах Bravo и Charlie в столбец первичного ключа в таблице Alpha. Это дает вам 1-ко-многим, а также позволяет вам указать, является ли отношение обязательным, просто установив обнуляемость столбца FK (что невозможно в вашем текущем проекте).

  • Добавить ограничение уникального ключа для таблиц Браво, Чарли и т. Д. В столбце AlphaID. Это создает отношение «1 к 1» с дополнительным преимуществом, заключающимся в том, что уникальный ключ также действует как индекс, который может помочь ускорить запросы, извлекающие строки на основе значения внешнего ключа.

Основным преимуществом этого подхода является то, что изменение легче:

  • Хотите обратно 1-ко-многим? Удалите соответствующий уникальный ключ или просто измените его на обычный индекс
  • Хотите, чтобы Браво существовало независимо от Альфы? У вас уже есть суррогатный ключ, все что вам нужно сделать, это установить столбец AlphaID FK, чтобы разрешить NULL
3 голосов
/ 11 сентября 2008

Лично я добился большого успеха с вашей второй моделью, используя PK / FK в одном столбце.

У меня никогда не было ситуации, когда все Альфы должны были иметь запись в таблице Браво или Чарли. Я всегда имел дело с 1 <-> 0..1, никогда 1 <-> 1.

Что касается вашего последнего вопроса, это просто еще много таблиц.

1 голос
/ 17 сентября 2008

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

  • система, которую легко изучать и обслуживать.

Какую «Систему» ​​будет легко изучать и обслуживать? Исходный код вашего приложения или данные приложения через интерфейс конечного пользователя?

  • В моей базе данных установлена ​​целостность данных.

Что вы подразумеваете под "принудительным применением в моей базе данных"? Означает ли это, что вы никоим образом не можете контролировать целостность данных, т. Е. Проект требует только правил целостности данных на основе БД?

  • схема, которая соответствует реальной логической организации моих данных.

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

  • классы / объекты в моем программировании, которые хорошо отображаются в таблицы базы данных (как в Linq to SQL)

Это требование звучит так, как будто ваша рука вынуждена создавать его с помощью linq to SQL, так ли это?

  • быстрые операции чтения и записи

Что такое "скоростной"? 0,03 секунды? 3 секунды? 30 минут? Это неясно, поскольку вы не указываете размер данных и тип операций, на которые ссылаетесь.

  • эффективное использование пробела (несколько пустых полей)

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

1 голос
/ 12 сентября 2008

У меня есть пример, который работает довольно хорошо, пока соответствует вашей модели:

У меня есть таблицы Чарли и Браво с внешним ключом alpha_id от Alpha. Как и в первом примере, за исключением того, что alpha не является первичным ключом, bravo_id и charlie_id имеют значение.

Я использую alpha_id в каждой таблице, которую мне нужно адресовать этим объектам, поэтому, чтобы избежать SQL, который может вызвать некоторую задержку, исследуя и Браво, и Чарли, чтобы найти, какая из них является Альфой, я создал таблицу AlphaType и в таблице Альфа I иметь свой идентификатор (alpha_type_id) в качестве внешнего ключа. Таким образом, я могу программным способом узнать, с каким AlphaType я имею дело, без объединения таблиц, которые могут содержать миллионы записей. в tSQL:

// For example sake lets think Id as a CHAR.
// and pardon me on any mistake, I dont have the exact code here,
// but you can get the idea

SELECT 
  (CASE alpha_type_id
    WHEN 'B' THEN '[Bravo].[Name]'
    WHEN 'C' THEN '[Charlie].[Name]'
    ELSE Null
  END)
FROM ...
1 голос
/ 11 сентября 2008

У вас может быть таблица соединения, в которой указывается альфа и связанный идентификатор. Затем вы можете добавить еще один столбец, указывающий, является ли это идентификатором Браво, Чарли или чего-либо еще. Сохраняет столбец Альфа, но добавляет сложности при объединении запросов.

1 голос
/ 11 сентября 2008

Еще один подход состоит в том, чтобы иметь 3 таблицы для хранения 3 сущностей и иметь отдельную таблицу для хранения отношений.

0 голосов
/ 17 сентября 2008

Я бы создал отношение супертип / подтип.

   THINGS
   ------
PK ThingId  

   ALPHAS
   ------
FK ThingId (not null, identifying, exported from THINGS)
   AlphaCol1
   AlphaCol2
   AlphaCol3  

   BRAVOS
   ------
FK ThingId (not null, identifying, exported from THINGS)
   BravoCol1
   BravoCol2
   BravoCol3  

   CHARLIES
   --------
FK ThingId (not null, identifying, exported from THINGS)
   CharlieCol1
   CharlieCol2
   CharlieCol3

Так, например, альфа, в которой есть Чарли, но не браво: -

insert into things values (1);
insert into alphas values (1,'alpha col 1',5,'blue');
insert into charlies values (1,'charlie col 1',17,'Y');

Обратите внимание, что вы не можете создать более одного Чарли для альфы, как если бы вы попытались создать два чарли с ThingId = 1, вторая вставка получит уникальное нарушение индекса / ограничения.

0 голосов
/ 11 сентября 2008

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

Место, где вы можете столкнуться с проблемами, - это если Браво вряд ли понадобится (<% 10) И вам нужно быстро выполнить запрос по одному из его полей, чтобы индексировать его.

...