Каков наилучший способ управления отношениями один к одному в SQL?

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

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

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

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

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

PK = primary key  
FK = foreign key  
NU = nullable

Одна таблица со многими полями nullalbe (плоский файл) & hellip;

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

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

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

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

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

Лучшее (или худшее) для обоих: множество внешних ключей nullalbe для многих таблиц & 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;?


EDIT: Это часть вопроса: Какая лучшая схема базы данных для моей навигации?

Ответ 1

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

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

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

Если Bravos и Charlies должны быть взаимоисключающими, простейший метод, вероятно, создаст поле дискриминатора:

      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 заставляет записи всегда принадлежать точно одному подтипу.

Ответ 2

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

  • Каждый объект должен иметь свой собственный первичный ключ, поэтому ваши таблицы Bravo, Charlie и др. должны определять свой собственный суррогатный ключ в дополнение к столбцу внешнего ключа для таблицы Alpha. Вы делаете свою модель домена довольно негибкой, указав, что первичный ключ одной таблицы должен быть точно таким же, как первичный ключ другой таблицы. Таким образом, сущности очень тесно связаны друг с другом, и одна сущность не может существовать без другой, что не является бизнес-правилом, которое необходимо применять в рамках проектирования базы данных.

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

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

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

  • Хотите вернуть 1-ко-многим? Отбросьте соответствующий уникальный ключ или просто измените его на обычный индекс.
  • Хотите, чтобы Bravo существовал независимо от Alpha? У вас уже есть ключ суррогата, все, что вам нужно, это установить столбец AlphaID FK, чтобы разрешить NULL

Ответ 3

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

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

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

Ответ 4

Еще один подход состоит из трех таблиц для хранения трех объектов и наличия отдельной таблицы для хранения отношений.

Ответ 5

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

Ответ 6

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

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

Я использую alpha_id для каждой таблицы, которую мне нужно адресовать этим сущностям, поэтому, чтобы избежать SQL, который может вызвать некоторую задержку, исследуя как Bravo, так и Charlie, чтобы найти, какой из них Alpha, я создал таблицу AlphaType и таблицу Alpha я имеют свой id (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 ...

Ответ 7

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

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

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

  • целостность данных, введенная в действие в моей базе данных.

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

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

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

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

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

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

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

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

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

Ответ 8

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

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

Ответ 9

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

   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

Итак, например, альфа, которая имеет charlie, но не bravo: -

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');

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