Дизайн таблиц и иерархии классов - PullRequest
2 голосов
/ 10 ноября 2010

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

abstract class Card{
    private $_name = '';
    private $_text = '';
}

class MtgCard extends Card{
    private $_manaCost = '';
    private $_power = 0;
    private $_toughness = 0;
    private $_loyalty = 0;
}

class PokemonCard extends Card{
    private $_energyType = '';
    private $_hp = 0;
    private $_retreatCost = 0;
}

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

TABLE Card
  id            INT, AUTO_INCREMENT, PK
  name          VARCHAR(255)
  text          TEXT

TABLE MtgCard
  id            INT, AUTO_INCREMENT, PK
  card_id       INT, FK(card.id)
  manacost      VARCHAR(32)
  power         INT
  toughness     INT
  loyalty       INT

TABLE PokemonCard
  id            INT, AUTO_INCREMENT, PK
  card_id       INT, FK(card.id)
  hp            INT
  energytype    ENUM(...)
  retreatcost   INT

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

Должен ли я добавить столбец VARCHAR в Card, чтобы он содержал имя связанной таблицы? Это единственное решение, к которому пришли мои коллеги и я, но оно кажется слишком «грязным». Ключом здесь является возможность расширения дизайна, что позволяет легко добавлять новые подклассы .

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

Ответы [ 2 ]

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

Google "Обобщение специализации реляционного моделирования". Вы найдете несколько отличных статей на тему того, как смоделировать паттерн gen-spec с использованием реляционных таблиц. Этот же вопрос задавался много раз в SO, с немного другими деталями.

Лучшие из этих статей подтвердят ваше решение иметь одну таблицу для обобщенных данных и отдельные таблицы для специализированных данных. Самым большим отличием будет то, как они рекомендуют использовать первичные и внешние ключи. Как правило, они рекомендуют, чтобы специализированные таблицы имели один столбец, который выполняет двойную функцию. Он служит первичным ключом для специализированной таблицы, но также является внешним ключом, который дублирует PK обобщенной таблицы.

Это немного сложно поддерживать, но это очень мило во время соединения.

Также имейте в виду, что DDL требуется при добавлении нового класса в иерархию.

3 голосов
/ 11 ноября 2010

В основном нет.

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

Если вам нужна мощь и гибкость (в частности, расширяемость) реляционной базы данных, вам нужно моделировать ее независимо от любого приложения и с использованием принципов RDb, а не требований к языку приложения. Оставьте контекст своего приложения на некоторое время и создайте базу данных как базу данных. Узнайте о них. Нормализовать (устранить все дублирование). Узнайте о структурах и правилах и реализуйте их. Когда вы это сделаете, ваши запросы и ваше «отображение», будет легко. Там не будет никакого "импеданса". Используйте правильные типы данных, и несоответствия не будет.

Требуемая структура - это обычный подтип-супертип. Это термины, относящиеся к реляционной базе данных, которые существуют в РМ более 30 лет, а в продуктах реляционной базы данных - более 23 лет. Не нужно называть их забавными новыми именами. Вики не является академическим справочником, за исключением, может быть, раковых опухолей.

Учитывая ваши таблицы, которые являются правильной отправной точкой (вы автоматически нормализовались), вам нужно:

  • Переименовать карту. Идентифицировать как Card.CardId

  • Удалите идентификаторы для подтипов, они на 100% избыточны; CardId - это и PK, и FK.

  • Добавьте дискриминатор Card.CardType CHAR (1) или TINYINT. Это определит, к какому подтипу присоединиться, когда CardType неизвестен.

  • Похоже, вы не до конца понимаете концепцию внешних ключей, так что было бы неплохо начать сначала. Он реализован здесь в своей простой, обычной форме:

<code>    ALTER TABLE MtgCard
        ADD CONSTRAINT Card_MtgCard_fk
        FOREIGN KEY (CardId)
        REFERENCES Card(CardId)
  • Отношение между Card и MtgCard или PokemonCard всегда равно 1 :: 1. Супертип завершен только тогда, когда есть карта плюс {MtgCard | PokemonCard} с тем же CardId. В вашем случае может быть только один подтип, который легко реализовать с помощью простого ограничения CHECK.

    • В других случаях , более одного подтипа вполне законно.

    • Подтипы: Человек - учитель или Человек - ученик

  • В реляционных базах данных нет концепции объединения «от» или «к» (или вверх / вниз или влево / вправо), эти понятия существуют только для того, чтобы помочь нам, людям; Вы можете начать с любой таблицы / ключа и перейти к любой таблице. Промежуточные таблицы требуются только в отсутствие реляционных идентификаторов (т. Е. Где дополнительные суррогаты, столбцы идентификаторов, используются в качестве PK вместо значимых естественных ключей).

    • В этом примере, используя ваши термины, вы можете перейти прямо с Регистрация до Person (например, чтобы захватить LastName) или для Курс (чтобы захватить Имя) без посещения промежуточных столов; линии связи сплошные.
      ,
  • Теперь иерархии классов («Есть» или «Есть») и все остальное просты и не требуют усилий. Если это не кажется вам простым, пожалуйста, опубликуйте предикат класса с одним оператором (на английском языке), и я предоставлю SQL.

Краткое руководство к стандартным диаграммам реляционной базы данных.

...