Подтвердить что ты не робот

Как вы относитесь к полиморфизму в базе данных?

Пример

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


Как вы могли бы решить эту проблему? У вас есть таблица Person, которая хранит все данные, общие для человека, и использует ключ для поиска своих данных в SpecialPerson (если они являются особым человеком) и Пользователем (если они являются пользователем) и наоборот

4b9b3361

Ответ 1

Обычно существует три способа сопоставления наследования объектов с таблицами базы данных.

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

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

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

То, что вы хотите использовать, зависит, конечно, от вашей ситуации. Ни одно из решений не идеально подходит, поэтому вам нужно взвесить плюсы и минусы.

Ответ 2

Взгляните на Мартина Фаулера Шаблоны архитектуры корпоративных приложений:

  • Наследование отдельных таблиц:

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

  • Наследование классов таблицы:

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

  • Наследование бетонных таблиц:

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

Ответ 3

То, что я собираюсь здесь сказать, собирается отправлять архитекторов баз данных в conniptions, но здесь идет:

Рассмотрим представление базы данных как эквивалент определения интерфейса. И таблица эквивалентна классу.

Итак, в вашем примере все классы из 3 человек будут реализовывать интерфейс IPerson. Таким образом, у вас есть 3 таблицы - по одному для каждого из "Пользователь", "Человек" и "Специальный игрок".

Затем у вас есть представление "PersonView" или что-то другое, которое выбирает общие свойства (как определено вашим "интерфейсом" ) из всех трех таблиц в один вид. Используйте столбец "PersonType" в этом представлении, чтобы сохранить фактический тип хранимого человека.

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

Ответ 4

Возможно, это не то, о чем хотел спросить OP, но я подумал, что я могу бросить это здесь.

Недавно у меня был уникальный случай полиморфизма db в проекте. У нас было от 60 до 120 возможных классов, каждый со своим набором из 30-40 уникальных атрибутов и около 10 - 12 общих атрибутов для всех классов. Мы решили пойти по пути SQL-XML и оказались в одной таблице. Что-то вроде:

PERSON (personid,persontype, name,address, phone, XMLOtherProperties)

содержащий все общие свойства в виде столбцов, а затем большой пакет свойств XML. Затем слой ORM отвечал за чтение/запись соответствующих свойств из XMLOtherProperties. Немного нравится:

 public string StrangeProperty
{
get { return XMLPropertyBag["StrangeProperty"];}
set { XMLPropertyBag["StrangeProperty"]= value;}
}

(мы закончили сопоставление столбца xml как Hastable, а не XML-документа, но вы можете использовать все, что подходит вашему DAL)

Он не выиграет никаких дизайнерских наград, но он будет работать, если у вас есть большое (или неизвестное) количество возможных классов. И в SQL2005 вы все равно можете использовать XPATH в своих SQL-запросах для выбора строк на основе некоторого свойства, которое хранится как XML. Это просто небольшое снижение производительности.

Ответ 5

Если у Пользователя, Лица и Специального лица все одинаковые внешние ключи, у меня будет одна таблица. Добавьте столбец под названием "Тип", который ограничен для пользователя, лица или специального лица. Затем на основе значения типа есть ограничения на другие необязательные столбцы.

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

Ответ 6

Я бы сказал, что, в зависимости от того, что отличает Person и Special Person, вы, вероятно, не хотите полиморфизма для этой задачи.

Я бы создал таблицу User, таблицу Person, которая имеет поле NULL-ключа для пользователя (т.е. пользователь может быть пользователем, но не должен).
Затем я сделаю таблицу SpecialPerson, которая относится к таблице Person с любыми дополнительными полями в ней. Если в SpecialPerson присутствует запись для данного Person.ID, он/она/это специальный человек.

Ответ 7

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

  • Таблица для иерархии классов. Одна таблица для всей иерархии.
  • Таблица для каждого подкласса. Отдельная таблица создается для каждого подкласса с ассоциацией 0-1 между подклассовыми таблицами.
  • Таблица для конкретного класса. Для каждого конкретного класса создается отдельная таблица.

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

Ответ 8

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

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

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

Итак, мой дизайн будет чем-то вроде

ЧЕЛОВЕК (лицо, имя, адрес, телефон,...)

SPECIALPERSON (personid СПИСОК ЛИЧНОСТИ (personid), дополнительные поля...)

USER (personid REFERENCES PERSON (personid), имя пользователя, encryptedpassword, дополнительные поля...)

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

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

Ответ 9

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

Я бы пошел с таблицей для каждого подкласса, а также избегал хитов производительности, но используя ORM, где мы можем избежать объединения со всеми таблицами подкласса, создавая запрос "на лету", основываясь на типе. Вышеупомянутая стратегия работает для выталкивания одиночного уровня записи, но для массового обновления или выбора вы не можете избежать этого.

Ответ 10

да, я бы также рассмотрел TypeID вместе с таблицей PersonType, если возможно, будет больше типов. Однако, если есть только 3, которые не должны быть указаны.

Ответ 11

Это более старая должность, но я думал, что буду взвешивать с концептуальной, процедурной и служебной точки зрения.

Первый вопрос, который я задал бы, - это отношения между человеком, специальным человеком и пользователем, и может ли кто-то быть одновременно и специальным человеком, и пользователем. Или любая из 4 возможных комбинаций (класс a + b, класс b + c, класс a + c или + b + c). Если этот класс хранится как значение в поле type и поэтому приведет к краху этих комбинаций, и этот коллапс неприемлем, тогда я думаю, что потребуется вторичная таблица, позволяющая установить отношения "один ко многим". Я узнал, что вы не судите об этом, пока не оцените использование и стоимость потери информации о комбинации.

Другим фактором, который заставляет меня наклониться к одной таблице, является ваше описание сценария. User - единственная сущность с именем пользователя (скажем, varchar (30)) и пароль (скажем, varchar (32)). Если допустимая длина общих полей равна 20 символам на 20 полей, то увеличение размера столбца составляет 62 более 400, или около 15% - 10 лет назад, это было бы более дорогостоящим, чем в современных системах РСУБД, особенно с такой тип поля, как varchar (например, для MySQL).

И если безопасность вас беспокоит, может быть полезно иметь вторичную таблицу "один-к-одному", называемую credentials ( user_id, username, password). Эта таблица будет вызвана в JOIN контекстно в момент времени входа в систему, но структурно отделена от "всего" в главной таблице. И LEFT JOIN доступен для запросов, которые могут захотеть рассмотреть "зарегистрированных пользователей".

Моим главным соображением в течение многих лет остается рассмотреть значение объекта (и, следовательно, возможную эволюцию) вне БД и в реальном мире. В этом случае все типы людей бьют сердца (я надеюсь), а также могут иметь иерархические отношения друг с другом; поэтому, в глубине моего сознания, даже если не сейчас, нам может потребоваться сохранить такие отношения другим методом. Это явно не связано с вашим вопросом здесь, но это еще один пример выражения отношения объекта. И к настоящему времени (7 лет спустя) вы должны иметь хорошее представление о том, как ваше решение работало в любом случае:)

Ответ 12

Лично я бы сохранил все эти разные пользовательские классы в одной таблице. Затем у вас может быть поле, в котором хранится значение "Тип", или вы можете указать, с какими людьми вы сталкиваетесь, по каким полям заполняется. Например, если UserID равен NULL, тогда эта запись не является Пользователь.

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

Первый метод также поддерживается LINQ-to-SQL, если вы решили спуститься по этому маршруту (они называются "Таблица на иерархию" или "TPH" ).

Ответ 13

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