Наследование от одной таблицы

Автор: | 23.07.2018

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

Я думаю о том, как представлять сложную структуру в базе данных SQL Server.

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

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

Я вижу, что есть два основных варианта:

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

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

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

Какова наилучшая практика для этого сценария?

@Bill Karwin описывает три модели наследования в своей книге SQL Antipatterns, когда предлагая решения для SQL Entity-Attribute-Value antipattern. Это краткий обзор:

Наследование отдельных таблиц (aka Table In Heary Inheritance):

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

Сохранение простой конструкции — это плюс, но основными проблемами с этим подходом являются следующие:

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

База данных не сможет обеспечить, какие атрибуты применяются, а какие нет, поскольку нет метаданных для определения того, какие атрибуты принадлежат к подтипам.

Вы также не можете применять NOT NULL атрибуты подтипа, которые должны быть обязательными. Вам придется обрабатывать это в своем приложении, что в целом не идеально.

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

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

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

Теперь обязательные атрибуты можно выполнить с помощью NOT NULL .

Добавление нового подтипа требует добавления новой таблицы вместо добавления столбцов в существующую.

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

Атрибут type не нужен, как в методе одиночной таблицы. Тип теперь определяется метаданными: имя таблицы.

Однако эта модель также имеет несколько недостатков:

Общие атрибуты смешиваются с атрибутами подтипа, и нет простого способа их идентифицировать. База данных также не будет знать.

При определении таблиц вам придется повторять общие атрибуты для каждой таблицы подтипов. Это определенно не DRY.

Поиск всех политик, независимо от подтипа, становится трудным, и для него потребуется группа UNION s.

Вот как вам придется запрашивать все политики независимо от типа:

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

Наследование классов таблицы (aka Table per Type Inheritance):

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

Третий вариант — создать таблицу «Политика», а затем таблицу «SectionsMain», в которой хранятся все поля, которые являются общими для типов разделов. Затем создайте другие таблицы для каждого типа раздела, которые содержат только те поля, которые не являются общими.

Выбор, который лучше всего зависит в основном от того, сколько полей у вас есть и как вы хотите написать свой SQL. Все будут работать. Если у вас всего несколько полей, я, вероятно, поеду с №1. С «лотами» полей я бы наклонился к № 2 или № 3.

С предоставленной информацией я бы смоделировал базу данных, чтобы иметь следующее:

  • POLICY_ID (первичный ключ)
  • LIABILITY_ID (первичный ключ)
  • POLICY_ID (внешний ключ)
  • PROPERTY_ID (первичный ключ)
  • POLICY_ID (внешний ключ)

. и т.д., потому что я ожидаю, что будут разные атрибуты, связанные с каждым разделом политики. В противном случае может быть одна таблица SECTIONS , а в дополнение к policy_id будет section_type_code .

Читайте так же:  Перерасчет отопления 1 раз в год

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

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

Поскольку SQL основан на SET, он скорее чужд процедурных/OO-концепций программирования и требует перехода кода из одной области в другую. ORM часто рассматриваются, но они плохо работают в сложных и сложных системах.

Single Table Inheritance (Наследование с единой таблицей)

Паттерн проектирования Single Table Inheritance

Описание Single Table Inheritance

Представление всех классов из иерархии наследования в виде одной таблицы в БД, содержащей столбцы для всех полей различных классов.

Реляционные БД не поддерживают наследование, по этому, записывая данные об объектах в БД, мы вынуждены придумывать, как отобразить наследование в таблицах. Конечно, мы стараемся минимизировать JOIN’ы, которые мнговенно появятся, если наследование реализовывать несколькими таблицами в БД. Паттерн Single Table Inheritance (наследование с единой таблицей) записывает все поля всех классов иерархии в одну таблицу.

Использована иллюстрация с сайта Мартина Фаулера.

Наследование моделей — продумываю архитектуру

Наследование моделей — продумываю архитектуру

Сообщение mithrandir » 2014.06.14, 20:45

Всё заработало очень круто и красиво, однако наткнулся на следующие подводные камни, касающиеся общего функционала для всех типов проектов:

1. При сквозном поиске записей разных типов не понятно какую модель использовать. Я использую корневую:

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

2. К примеру, у меня есть разные формы для редактирования разных типов проектов, но они ссылаются на один экшен:

Наследование от одной таблицы

PostgreSQL ТЕБМЙЪХЕФ ОБУМЕДПЧБОЙЕ ФБВМЙГ, ЛПФПТПЕ НПЦЕФ ПЛБЪБФШУС РПМЕЪОЩН ЙОУФТХНЕОФПН ДМС ТБЪТБВПФЮЙЛПЧ ВБЪЩ ДБООЩИ. (чПЪНПЦОПУФШ ОБУМЕДПЧБОЙС ПРТЕДЕМСЕФУС УФБОДБТФПН SQL:1999 Й ВПМЕЕ РПЪДОЙНЙ УФБОДБТФБНЙ Й ЧП НОПЗЙИ ПФОПЫЕОЙСИ ПФМЙЮБЕФУС ПФ ЧПЪНПЦОПУФЕК, ПРЙУЩЧБЕНЩИ ЪДЕУШ.)

оБЮОЈН У РТЙНЕТБ: РТЕДРПМПЦЙН НЩ РЩФБЕНУС УПЪДБФШ НПДЕМШ ДБООЩИ ДМС ЗПТПДПЧ. ч ЛБЦДПН ЫФБФЕ ЕУФШ ОЕУЛПМШЛП ЗПТПДПЧ, ОП ФПМШЛП ПДОБ УФПМЙГБ. нЩ ИПФЙН РТЕДПУФБЧЙФШ ЧПЪНПЦОПУФШ ВЩУФТПЗП ОБИПЦДЕОЙС ЗПТПДБ-УФПМЙГЩ ДМС МАВПЗП ПФДЕМШОПЗП ЫФБФБ. чУЈ ЬФП НПЦОП УДЕМБФШ, УПЪДБЧ ДЧЕ ФБВМЙГЩ, ПДОХ ДМС УФПМЙГ ЫФБФБ Й ДТХЗХА ДМС ЗПТПДПЧ, ЛПФПТЩЕ ОЕ СЧМСАФУС УФПМЙГБНЙ. пДОБЛП, ЮФП РТПЙЪПКДЈФ, ЛПЗДБ НЩ ЪБИПФЙН РПМХЮЙФШ ДБООЩЕ П ЛБЛПН-МЙВП ЗПТПДЕ, Ч ОЕ ЪБЧЙУЙНПУФЙ ПФ ФПЗП, СЧМСЕФУС ПО УФПМЙГЕК ЙМЙ ОЕФ? чПЪНПЦОПУФШ ОБУМЕДПЧБОЙС НПЦЕФ РПНПЮШ ТЕЫЙФШ ЬФХ РТПВМЕНХ. нЩ УПЪДБЈН ФБВМЙГ i capitals , ЛПФПТБС ОБУМЕДХЕФ ПФ ФБВМЙГЩ cities :

CREATE TABLE cities ( name text, population float, altitude int — (in ft) ); CREATE TABLE capitals ( state char(2) ) INHERITS (cities);

ч ЬФПН УМХЮБЕ, ФБВМЙГБ capitals ОБУМЕДХЕФ ЧУЕ ЛПМПОЛЙ ЙЪ ТПДЙФЕМШУЛПК ФБВМЙГЩ cities . ч ФБВМЙГЕ УФПМЙГ ЫФБФПЧ ФБЛЦЕ ЕУФШ ДПРПМОЙФЕМШОБС ЛПМПОЛБ У БВВТЕЧЙБФХТПК ОБЪЧБОЙС ЫФБФБ — state .

ч PostgreSQL МАВБС ФБВМЙГБ НПЦЕФ ОБУМЕДПЧБФШ ПФ ОХМС ЙМЙ ВПМЕЕ ДТХЗЙИ ФБВМЙГ, Б ЪБРТПУ НПЦЕФ УУЩМБФШУС МЙВП ОБ ЧУЕ УФТПЛЙ Ч ФБВМЙГЕ МЙВП ОБ ЧУЕ УФТПЛЙ Ч ФБВМЙГЕ ЧНЕУФЕ У ЕЈ РПФПНЛБНЙ. рПУМЕДОЕЕ СЧМСЕФУС РПЧЕДЕОЙЕН РП ХНПМЮБОЙА. оБРТЙНЕТ, УМЕДХАЭЙК ЪБРТПУ ЙЭЕФ ЙНЕОБ ЧУЕИ ЗПТПДПЧ, ЧЛМАЮБСС УФПМЙГЩ ЫФБФПЧ, ЛПФПТЩЕ ТБУРПМПЦЕОЩ ОБ ЧЩУПФЕ УЧЩЫЕ 500 ЖХФПЧ:

SELECT name, altitude FROM cities WHERE altitude > 500;

у ДБООЩНЙ ДМС РТЙНЕТБ, ЧЪСФЩНЙ ЙЪ ХЮЕВОПЗП ТХЛПЧПДУФЧБ PostgreSQL (УН. Section 2.1 ), ЬФПФ ЪБРТПУ ЧПЪЧТБФЙФ:

name | altitude ————+———- Las Vegas | 2174 Mariposa | 1953 Madison | 845

у ДТХЗПК УФПТПОЩ, УМЕДХАЭЙК ЪБРТПУ ОБИПДЙФ ЧУЕ ЗПТПДБ, ЛПФПТЩЕ ОЕ СЧМСАФУС УФПМЙГБНЙ ЫФБФПЧ Й ЛПФПТЩЕ ФБЛЦЕ ТБУРПМПЦЕОЩ ОБ ЧЩУПФЕ УЧЩЫЕ 500ft:

SELECT name, altitude FROM ONLY cities WHERE altitude > 500; name | altitude ————+———- Las Vegas | 2174 Mariposa | 1953

ъДЕУШ УМПЧП «ONLY» РЕТЕД ФБВМЙГЕК cities ЗПЧПТЙФ, ЮФП ЪБРТПУ ДПМЦЕО ЧЩРПМОСФШУС ФПМШЛП ДМС ФБВМЙГЩ cities, Б ОЕ ДМС ФБВМЙГ, ТБРПМПЦЕООЩИ ОЙЦЕ cities, Ч ЙЕТБТИЙЙ ОБУМЕДПЧБОЙС. нОПЗЙЕ ЙЪ ЛПНБОД, ЛПФПТЩЕ НЩ ЙУРПМШЪПЧБМЙ ТБОЕЕ — SELECT , UPDATE Й DELETE — РПДДЕТЦЙЧБАФ ОПФБГЙА «ONLY» .

ч ОЕЛПФПТЩИ УМХЮБСИ, ЧЩ НПЦЕФЕ ЪБИПФЕФШ ХЪОБФШ ЙЪ ЛБЛПК ФБВМЙГЩ РПМХЮЕОБ ПФДЕМШОБС УФТПЛБ ТЕЪХМШФБФБ. ч ЛБЦДПК ФБВМЙГЕ УХЭЕУФЧХЕФ УЙУФЕНОБС ЛПМПОЛБ У ЙНЕОЕН tableoid , ЛПФПТБС ТБУУЛБЦЕФ П ФБВМЙГЕ:

SELECT c.tableoid, c.name, c.altiude FROM cities c WHERE c.altiude > 500;

tableoid | name | altiude ———-+————+———- 139793 | Las Vegas | 2174 139793 | Mariposa | 1953 139798 | Madison | 845

(еУМЙ ЧЩ РПРЩФБЕФЕУШ ЧПУРТПЙЪЧЕУФЙ ЬФПФ РТЙНЕТ, ФП РТЕДРПМПЦЙФЕМШОП ЧЩ РПМХЮЙФЕ ДТХЗЙЕ ЪОБЮЕОЙС OID.) чЩРПМОЙЧ ПВЯЕДЙОЕОЙЕ У ФБВМЙГЕК pg_class ЧЩ НПЦЕФЕ ХЧЙДЕФШ УБНЙ ЙНЕОБ ФБВМЙГ:

SELECT p.relname, c.name, c.altitude FROM cities c, pg_class p WHERE c.altiude > 500 and c.tableoid = p.oid;

relname | name | altitude ———-+————+———- cities | Las Vegas | 2174 cities | Mariposa | 1953 capitals | Madison | 845

оБУМЕДПЧБОЙЕ ОЕ ТБУРТПУФТБОСЕФУС БЧФПНБФЙЮЕУЛЙ ОБ ДБООЩЕ ЙЪ ЛПНБОД INSERT ЙМЙ COPY ОБ ДТХЗЙЕ ФБВМЙГЩ Ч ЙЕТБТИЙЙ ОБУМЕДПЧБОЙС. ч УМЕДХАЭЕН РТЙНЕТЕ, ЧЩРПМОЕОЙЕ INSERT ЧЩЪПЧЕФ ПЫЙВЛХ:

INSERT INTO cities (name, population, altitude, state) VALUES (‘New York’, NULL, NULL, ‘NY’);

нЩ НПЗМЙ ВЩ ОБДЕСФШУС, ЮФП ДБООЩЕ ЛБЛ-ОЙВХДШ ВХДХФ РЕТЕОБРТБЧМЕОЩ Ч ФБВМЙГХ capitals , ОП ЬФПЗП ОЕ РТПЙЪПКДЈФ: INSERT ЧУЕЗДБ ЧУФБЧМСЕФ ДБООЩЕ ФПЮОП Ч ХЛБЪБООХА ФБВМЙГХ. ч ОЕЛПФПТЩИ УМХЮБИ, ЧПЪНПЦОП РЕТЕОБРТБЧЙФШ ЧУФБЧМСЕНЩЕ ДБООЩЕ, У РПНПЭША РТБЧЙМБ (УН Chapter 34 ). пДОБЛП, ЬФП ОЕ РПНПЦЕФ Ч ДБООПН ЧЩЫЕ УМХЮБЕ, РПФПНХ ЮФП ФБВМЙГБ cities ОЕ УПДЕТЦЙФ ЛПМПОЛЙ state Й ФБЛЙН ПВТБЪПН ЛПНБОДБ ВХДЕФ ПФЧЕТЗОХФБ, РЕТЕД ФЕН ЛБЛ Л ОЕК НПЦОП ВХДЕФ РТЙНЕОЙФШ РТБЧЙМП.

Читайте так же:  Когда подавать документы на единовременное пособие по рождению ребенка

дМС ФБВМЙГ ЧОХФТЙ ЙЕТБТИЙЙ ОБУМЕДПЧБОЙС, НПЗХФ ВЩФШ ЪБДБОЩ ПЗТБОЙЮЕОЙС ГЕМПУФОПУФЙ check. чУЕ ПЗТБОЙЮЕОЙС check ДМС ТПДЙФЕМШУЛПК ФБВМЙГЩ БЧФПНБФЙЮЕУЛЙ ОБУМЕДХАФУС ЧУЕНЙ ЕЈ РПФПНЛБНЙ. пДОБЛП, ДТХЗЙЕ ФЙРЩ ПЗТБОЙЮЕОЙК ГЕМПУФОПУФЙ ОЕ ОБУМЕДХАФУС.

фБВМЙГБ НПЦЕФ ОБУМЕДПЧБФШ ВПМЕЕ ЮЕН ПФ ПДОПК ТПДЙФЕМШУЛПК ФБВМЙГЩ Й Ч ЬФПН УМХЮБЕ ПОБ ВХДЕФ УПДЕТЦБФШ УХННБТОЩК УРЙУПЛ ЛПМПОПЛ ЙЪ ТПДЙФЕМШУЛЙИ ФБВМЙГ. л ЬФПНХ УРЙУЛХ ДПВБЧМСАФУС МАВЩЕ ЛПМПОЛЙ, ЪБДБЧБЕНЩЕ Ч УБНПК ФБВМЙГЕ-РПФПНЛЕ. еУМЙ Ч ОЕУЛПМШЛЙИ ТПДЙФЕМШУЛЙИ ФБВМЙГБИ, ЧУФТЕЮБАФУС ЛПМПОЛЙ У ПДЙОБЛПЧЩН ЙНЕОЕН ЙМЙ ФБЛБС ЛПМПОЛБ ЕУФШ Ч ТПДЙФЕМШУЛПК ФБВМЙГЕ Й Ч ПРТЕДЕМЕОЙЙ ФБВМЙГЩ-РПФПНЛБ, ФП ЬФЙ ЛПМПОЛЙ «УМЙЧБАФУС» ФБЛЙН ПВТБЪПН, ЮФП ФПМШЛП ПДОБ ФБЛБС ЛПМПОЛБ ВХДЕФ Ч ФБВМЙГЕ-РПФПНЛЕ. рТЙ УМЙСОЙЙ, ЛПМПОЛЙ ДПМЦОЩ ЙНЕФШ ПДЙОБЛПЧЩК ФЙР, ЙОБЮЕ ВХДЕФ ЧЩДБОП УППВЭЕОЙЕ ПВ ПЫЙВЛЕ. лПМПОЛБ РПУМЕ УМЙСОЙС РПМХЮБЕФ ЛПРЙА ЧУЕИ ПЗТБОЙЮЕОЙК ГЕМПУФОПУФЙ check ПФ ЛБЦДПК УМЙФПК ЛПМПОЛЙ.

рПДИПДСЭЙК УРПУПВ УПЪДБОЙС ОПЧПК УПЧНЕУФЙНПК ФБВМЙГЩ ДМС ФБВМЙГЩ-РПФПНЛБ УПУФПЙФ Ч ЙУРПМШЪПЧБОЙЙ ПРГЙЙ LIKE Ч ЛПНБОДЕ CREATE TABLE . фБЛБС ЛПНБОДБ УПЪДБЈФ ФБВМЙГХ У ФЕНЙ ЦЕ ЛПМПОЛБНЙ Й У ФЕНЙ ЦЕ ФЙРБНЙ (УНПФТЙФЕ ПДОБЛП ЪБНЕЮБОЙЕ РТП РТЕДПУФЕТЕЦЕОЙС ОЙЦЕ). ч ЛБЮЕУФЧЕ БМШФЕТОБФЙЧЩ, УПЧНЕУФЙНХА ФБВМЙГХ НПЦОП УПЪДБФШ ЕУМЙ УРЕТЧБ УПЪДБФШ ОПЧХА ФБВМЙГХ-РПФПН У РПНПЭША CREATE TABLE , Б ЪБФЕН ХДБМЙФШ УЧСЪШ ОБУМЕДПЧБОЙС ЮЕТЕЪ ALTER TABLE .

тПДЙФЕМШУЛБС ФБВМЙГБ ОЕ НПЦЕФ ВЩФШ ХДБМЕОБ РПЛБ УХЭЕУФЧХЕФ ИПФС ВЩ ПДОБ ФБВМЙГБ-РПФПНПЛ. еУМЙ ЧЩ ИПФЙФЕ ХДБМЙФШ ФБВМЙГХ Й ЧУЕИ ЕЈ РПФПНЛПЧ, ФП ОБЙВПМЕЕ РТПУФПК УРПУПВ УПУФПЙФ Ч ХДБМЕОЙЙ ТПДЙФЕМШУЛПК ФБВМЙГЩ У ПРГЙЕК CASCADE . еУМЙ ЛПМПОЛЙ Ч ФБВМЙГБИ-РПФПНЛБИ ОБУМЕДХАФУС ЙЪ ТПДЙФЕМШУЛЙИ ФБВМЙГ, ФП ЙИ ОЕМШЪС ХДБМЙФШ ЙМЙ ЙЪНЕОЙФШ.

рПУЛПМШЛХ РТБЧБ ДПУФХРБ ОЕ ОБУМЕДХАФУС БЧФПНБФЙЮЕУЛЙ, ФП ЛПЗДБ РПМШЪПЧБФЕМШ РЩФБЕФУС РПМХЮЙФШ ДПУФХР Л ТПДЙФЕМШУЛПК ФБВМЙГЕ, ПО ДПМЦЕО ЙМЙ ЙНЕФШ ЛБЛ НЙОЙНХН ФЕ ЦЕ РТБЧБ, ЮФП Й ДМС ФБВМЙГЩ-РПФПНЛБ ЙМЙ ДПМЦЕО ЙУРПМШЪПЧБФШ ОПФБГЙА «ONLY» . лПЗДБ ЧЩ ДПВБЧМСЕФЕ ОПЧХА ФБВМЙГХ Ч УХЭЕУФЧХАЭХА ЙЕТБТИЙА ОБУМЕДПЧБОЙС, ХВЕДЙФЕУШ ЮФП РТЕДПУФБЧЙМЙ ЧУЕ ОЕПВИПДЙНЩЕ РТБЧБ ДПУФХРБ ДМС ОЕЈ.

уЕТШЈЪОПЕ ПЗТБОЙЮЕОЙЕ ЧПЪНПЦОПУФЙ ОБУМЕДПЧБОЙС УПУФПЙФ Ч ФПН, ЮФП ЙОДЕЛУЩ (ЧЛМАЮБС ПЗТБОЙЮЕОЙС ХОЙЛБМШОПУФЙ) Й ЧОЕЫОЙЕ ЛМАЮЙ РТЙНЕОСАФУС ФПМШЛП Л ПДЙОПЮОЩН ФБВМЙГБН, Б ОЕ Л ОБУМЕДХАЭЙН ПФ ОЙИ РПФПНЛБН. ьФП ПЗТБОЙЮЕОЙЕ УРТБЧЕДМЙЧП ЛБЛ ДМС УУЩМБАЭЙИУС ФБЛ Й ДМС УУЩМПЮОЩИ УФПТПО ЧОЕЫОЕЗП ЛМАЮБ. фБЛЙН ПВТБЪПН, Ч ФЕТНЙОБИ ДБООПЗП ЧЩЫЕ РТЙНЕТБ:

еУМЙ ЧЩ ПРТЕДЕМСЕФЕ cities . name ЛБЛ UNIQUE ЙМЙ PRIMARY KEY , ФП ЬФЙ ПЗТБОЙЮЕОЙС ОЕ ПУФБОПЧСФ ОБМЙЮЙЕ Ч ФБВМЙГЕ capitals УФТПЛ, Ч ЛПФПТЩИ ЪОБЮЕОЙС name ДХВМЙТХАФ ЪОБЮЕОЙС Ч УФТПЛБИ ФБВМЙГЩ cities . й ЬФЙ ДХВМЙТХАЭЙЕУС УФТПЛЙ ВХДХФ РП ХНПМЮБОЙА РПЛБЪЩЧБФШУС Ч ЪБРТПУБИ Л ФБВМЙГЕ cities . жБЛФЙЮЕУЛЙ, РП ХНПМЮБОЙА ФБВМЙГБ capitals ОЕ ВХДЕФ ЙНЕФШ ОЙЛБЛЙИ ПЗТБОЙЮЕОЙК ХОЙЛБМШОПУФЙ Й ФБЛЙН ПВТБЪПН НПЦЕФ УПДЕТЦБФШ НОПЦЕУФЧП УФТПЛ У ПДОЙН Й ФЕН ЦЕ ЪОБЮЕОЙЕН name . чЩ НПЦЕФЕ ДПВБЧЙФШ ПЗТБОЙЮЕОЙЕ ХОЙЛБМШОПУФЙ ДМС ФБВМЙГЩ capitals , ОП ПОП ОЕ РТЕДПФЧТБФЙФ РПСЧМЕОЙЕ ДХВМЙТПЧБООЩИ ЪОБЮЕОЙК У ФБВМЙГЕК cities .

рПИПЦЙН ПВТБЪПН, ЕУМЙ НЩ ЪБДБМЙ ДМС cities . name ПЗТБОЙЮЕОЙЕ REFERENCES УП УУЩМЛПК ОБ ДТХЗХА ФБВМЙГХ, ЬФП ПЗТБОЙЮЕОЙЕ ОЕ ВХДЕФ БЧФПНБФЙЮЕУЛЙ ТБУРТПУФТБОСФУС ОБ ФБВМЙГХ capitals . ч ЬФПН УМХЮБЕ, ЧЩ НПЦЕФЕ ЧТХЮОХА ДПВБЧЙФШ ПЗТБОЙЮЕОЙЕ REFERENCES ДМС capitals .

еУМЙ Ч ДТХЗПК ФБВМЙГЕ ЛБЛБС-МЙВП ЛПМПОЛБ ЙНЕЕФ ПЗТБОЙЮЕОЙЕ REFERENCES cities(name) , ФП ПОП ВХДЕФ ТБВПФБФШ ФПМШЛП У ЪОБЮЕОЙЕН name Ч ФБВМЙГЕ cities , ОП ОЕ У ЪОБЮЕОЙЕН name Ч ФБВМЙГЕ capitals . ч ДБООПН УМХЮБЕ — ЬФП ОЕИПТПЫП.

оЕ УХЭЕУФЧХЕФ РПДИПДСЭЕЗП УРПУПВБ УПЪДБФШ ФБВМЙГХ, УПЧНЕУФЙНХА У ХЛБЪБООПК ТПДЙФЕМШУЛПК, ЧЛМАЮБС ЛПМПОЛЙ Й ПЗТБОЙЮЕОЙС ГЕМПУФОПУФЙ. пРГЙС LIKE ДМС ЛПНБОДЩ CREATE TABLE ОЕ ЛПРЙТХЕФ ПЗТБОЙЮЕОЙС ГЕМПУФОПУФЙ РТЙ УПЪДБОЙЙ ФБВМЙГ, ЮФП ДЕМБЕФ ЙИ ОЕЗПДОЩНЙ ДМС ДПВБЧМЕОЙС У РПНПЭША ALTER TABLE . уПЧРБДБАЭЙЕ ПЗТБОЙЮЕОЙС ГЕМПУФОПУФЙ check ДПМЦОЩ ДПВБЧМСФШУС ЧТХЮОХА ЙМЙ ФБВМЙГБ ДПМЦОБ ВЩФШ УПЪДБОБ ЛБЛ РПФПНПЛ УТБЪХ ЦЕ, ЪБФЕН, ЕУМЙ ОЕПВИПДЙНП, ЧТЕНЕООП ХДБМЕОБ ЙЪ УФТХЛФХТЩ ОБУМЕДПЧБОЙС ДМС ДПВБЧМЕОЙС УОПЧБ РПЪЦЕ.

еУМЙ ФБВМЙГБ ХЦЕ ХДБМЕОБ ЙЪ УФТХЛФХТЩ ОБУМЕДПЧБОЙС У РПНПЭША ALTER TABLE , ФП ЧУЕ ЕЈ ЛПМПОЛЙ ВХДХФ ПФНЕЮЕОЩ ЛБЛ ЪБДБООЩЕ МПЛБМШОП. ьФП ПЪОБЮБЕФ, ЮФП ЛПНБОДБ DROP COLUMN ОБ ТПДЙФЕМШУЛХА ФБВМЙГХ РТЙ ЛБУЛБДОПН ХДБМЕОЙЙ, ОЙЛПЗДБ ОЕ ЪБФТПОЕФ ЬФХ ФБВМЙГХ-РПФПНПЛ. лПМПЛЙ РТЙДЈФУС ХДБМСФШ ЧТХЮОХА.

дБООЩЕ ОЕДПУФБФЛЙ ЧПЪНПЦОП ВХДХФ ХУФТБОЕОЩ Ч ЛБЛЙИ-МЙВП ВХДХЭЙИ ЧЕТУЙСИ, ОП Ч ОБУФПСЭЕЕ ЧТЕНС ОЕПВИПДЙНП УПВМАДБФШ ПУФПТПЦОПУФШ РТЙ ЙУРПМШЪПЧБОЙЙ ОБУМЕДПЧБОЙС Ч ЧБЫЙИ ЪБДБЮБИ.

хУФБТЕЧЫЙЕ ПУПВЕООПУФЙ: ч РТЕДЩДХЭЙИ ЧЕТУЙСИ PostgreSQL ДП ЧЕТУЙЙ 7.1, РП ХНПМЮБОЙА, ПВТБВПФЛБ ФБВМЙГ-РПФПНЛПЧ Ч ЪБРТПУБИ ОЕ РТПЙЪЧПДЙМБУШ. вЩМП ТЕЫЕОП, ЮФП ЬФП СЧМСМСЕФУС ПЫЙВЛПК, Б ФБЛЦЕ ОЕ УППФЧЕФУФЧХЕФ УФБОДБТФХ SQL. чЩ НПЦЕФЕ РПМХЮЙФШ ФП РПЧЕДЕОЙЕ, ЮФП ВЩМП ДП ЧЕТУЙЙ 7.1 ЕУМЙ ЧЩЛМАЮЙФЕ ПРГЙА ЛПОЖЙЗХТБГЙЙ sql_inheritance .

Наследование от одной таблицы

18 августа 2010

Реляционные БД не поддерживают наследование, поэтому при отображении объекта на БД приходится как-то это обходить. При этом необходимо минимизировать количество JOIN. Решается данная проблема довольно простым способом при помощи паттерна наследование с одной таблицей. При этом, в таблице хранятся столбцы для всей ветки классов, наследуемых от заданного. Для определения типа модели обычно используется поле type.

В Yii этот паттерн реализуется достаточно красиво.

Наша схема с единственной таблицей:

Базовая модель AR с перекрытым методом instantiate:

Ну и наши наследники:

Для удобства в них переопределены defaultScope, то есть, например, при поиске с использованием FamilyCar будут найдены только семейные автомобили.

Читайте так же:  Можно ли по доверенности оформить ип

Комментарии RSS по email OK

Правильно ли я понял:

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

Мы также имеем служебное поле type в той же таблице для указания принадлежности конкретному классу;

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

Тогда, насколько я понимаю, для добавления нового нетривиального класса (такого, у которого есть новые поля) нам придется расширять общую таблицу, так? Это выглядит не очень-то гибким решением, если есть потребность добавлять новые классы объектов. Есть какие-нибудь способы это обойти?

Для добавления новых классов «на лету» без изменения структуры таблиц лучше посмотреть в сторону Entity-Attribute-Value, но тут будут как раз проблемы с JOIN (хотя на не очень крупных проектах сойдёт).

Спасибо за ссылку, общую идею понял.

По мне так это антипаттерн, плодить классы. какие плюсы? нежели просто отличать тип авто по атрибуту type?

Например, так можно заменить какой-нибудь if в методе для подсчёта стоимости модели на полиморфизм.

допустим нам нужно создать объект авто по параметру type. мне для этого фабрику тогда придется делать? а в ней тот же if или switch? или в контроллере условную логику сразу if ($type == Car::TYPE_SPORT) < $car = new SportCar; >else if .

Ну да, внутри метода instantiate будет всё тот же switch . Но это лучше, чем switch в жирном методе с логикой.

Чтобы избавиться от defaultScopes в каждом наследнике достаточно в родителе добавить

из-за get_called_class() работает только PHP > 5.3

Еще одно дополнение, лучше всего установить type для модели по умолчанию.

Нужно сделать это: 1) в методе model() 2) и в методе init()

Я бы хотел сделать Car abstract. Так и сделал у себя в коде пока не понадобилось сделать поиск типа:

В таком случае при abstract Car, PHP быстрее выстреливает ошибку о попытке создать объект асбтрактного класса.

Итого, есть ли способ сделать Car абстракным? 🙂

Наследование в postresql не применимо для собственно наследования?

Привет, лор. Нужен совет. Есть (будет) такая база

Очевидное содержимое таблиц очевидно:

Я не понимаю, как делать правильно и нахрена этот INHERIT нужен (кроме костылей с партиционированием, которые дико популярны на стаковерфлоу).

из доки постгреса: «A serious limitation of the inheritance feature is that indexes (including unique constraints) and foreign key constraints only apply to single tables, not to their inheritance children. This is true on both the referencing and referenced sides of a foreign key constraint». Т.е. «правильно» это не сделать никак, разве что рисовать костылищща с триггерами, изобретая велосипедный а-ля foreign key.

Нахрена нужен inherit — собственно, без внешних ключей почти все работает. Приятная иногда юзабельная плюшка.

postgresql наследование таблиц

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

итого общие параметры вынесены в отдельную таблицу:

события после того как накопятся в должном количестве еще и обсчитываются скопом, отсюда последний флаг.

и вот с постгрисом оказалось удобно очень, что родительская таблица позволяет выбрать одним простым запросом «все необработанные записи» из всех таблиц вообще.

но тут очень не хватает еще и знания в какой из дочерних таблиц лежат эти данные.

можно ли как-то SELECT’ом по родительской таблице получить в выборке дополнительный столбик — имя дочерней таблицы?

ооо, интересно такое есть в mysql?

в mysql много чего нет постгрисового. сейчас начал только с постгрисом возиться — дык рулез какой-то по сравнению с мускулем 🙂

правда некоторых вещей в нем нет INSERT ON DUPLICATE KEY например. можно создать правило, будет такой инсерт эмулировать, но не будет справляться с дублями внутри самого такого инсерта. а так все остальное — руль. например структуры и массивы (в т.ч. структур) в виде столбиков, процедуры на перле, возможность при INSERT сразу и обратно получить вставленные данные итп итд

кроме того я тут сваял тест: у меня вот эти самые устройства копят данные в БД. на тестовом хосте (десктоп слабая машинка) mysql на innodb делает примерно 200 инсертов в секунду. postgresql (при том что я в его админинге, в отличие от mysql ни бум бум еще, просто поставил) — делает 500. мне прямо это все подозрительным кажется. не бывает столько халявы-то зараз. где-то видимо расплатиться все равно придется 🙂

Конечно можно. Заведи ещё одно общее поле с типом устройства которое одновременно будет неявно указывать и на таблицу.

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