Поддержание целостности подкласса в реляционной базе данных - PullRequest
9 голосов
/ 02 декабря 2008

Допустим, у меня есть таблица, которая представляет суперкласс, студентов . А затем у меня есть N таблиц, которые представляют подклассы этого объекта ( спортсмены , музыканты и т. Д.). Как я могу выразить такое ограничение, чтобы ученик моделировался в одном (не более, не менее) подклассе?

Разъяснения относительно комментариев:

  • Это поддерживается вручную, а не через пакет ORM.
  • Проект, к которому это относится, расположен на SQL Server (но было бы неплохо увидеть общее решение)
  • Возможно, это не лучший пример. Есть несколько сценариев, которые мы можем рассмотреть в отношении подклассов, и я только что изобрел этот пример ученика / спортсмена.

А) В истинно объектно-ориентированной манере суперкласс может существовать сам по себе и не нуждается в моделировании в каких-либо подклассах.

B) В реальной жизни любой предмет или ученик может иметь несколько ролей.

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

Спасибо всем за ваш вклад, особенно Биллу.

Ответы [ 7 ]

3 голосов
/ 02 декабря 2008

Каждая запись учащегося будет иметь столбец подкласса (предположим, что это CHAR (1)). {A = Спортсмен, M = музыкант ...}

Теперь создайте свои таблицы спортсменов и музыкантов. У них также должен быть столбец SubClass, но должно быть ограничение проверки, жестко кодирующее значение для типа таблицы, которую они представляют. Например, вы должны установить значение по умолчанию «A» и ограничение CHECK «A» для столбца «Подкласс» в таблице «Спортсмен».

Свяжите свои таблицы Музыканта и Атлета с таблицей Студента, используя КОМПОЗИТНЫЙ внешний ключ StudentID AND Subclass. И вы сделали! Иди насладись чашечкой хорошего кофе.

CREATE TABLE Student (
    StudentID INT NOT NULL IDENTITY PRIMARY KEY,
    SubClass CHAR(1) NOT NULL,
    Name VARCHAR(200) NOT NULL,
    CONSTRAINT UQ_Student UNIQUE (StudentID, SubClass)
);

CREATE TABLE Athlete (
    StudentID INT NOT NULL PRIMARY KEY,
    SubClass CHAR(1) NOT NULL,
    Sport VARCHAR(200) NOT NULL,
    CONSTRAINT CHK_Jock CHECK (SubClass = 'A'),
    CONSTRAINT FK_Student_Athlete FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass)
);

CREATE TABLE Musician (
    StudentID INT NOT NULL PRIMARY KEY,
    SubClass CHAR(1) NOT NULL,
    Instrument VARCHAR(200) NOT NULL,
    CONSTRAINT CHK_Band_Nerd CHECK (SubClass = 'M'),
    CONSTRAINT FK_Student_Musician FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass)
);
2 голосов
/ 02 декабря 2008

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

CREATE TABLE athletes (
  student_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (student_id) REFERENCES students(student_id),
  CHECK (student_id NOT IN (SELECT student_id FROM musicians 
                      UNION SELECT student_id FROM slackers 
                      UNION ...)) 
);

edit: @JackPDouglas правильно указывает, что вышеуказанная форма ограничения CHECK не поддерживается Microsoft SQL Server. Фактически, в соответствии со стандартом SQL-99 недопустимо ссылаться на другую таблицу (см. http://kb.askmonty.org/v/constraint_type-check-constraint).

SQL-99 определяет объект метаданных для ограничений нескольких таблиц. Это называется ASSERTION , однако я не знаю ни одной СУБД, реализующей утверждения.

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

CREATE TABLE athletes (
  student_id INT NOT NULL PRIMARY KEY,
  student_type CHAR(4) NOT NULL CHECK (student_type = 'ATHL'),
  FOREIGN KEY (student_id, student_type) REFERENCES students(student_id, student_type)
);

Конечно, student_type может быть просто целым числом, я просто показываю его как символ для иллюстрации.

Если у вас нет поддержки CHECK ограничений (например, MySQL), то вы можете сделать нечто подобное в триггере.

Я прочитал ваше продолжение о том, чтобы убедиться, что в некоторой таблице подклассов есть строка для каждой строки в таблице суперкласса. Я не думаю, что есть практический способ сделать это с метаданными SQL и ограничениями. Единственный вариант, который я могу предложить для удовлетворения этого требования, - это использовать Single-Table Inheritance . В противном случае вам нужно полагаться на код приложения для обеспечения его соблюдения.

edit: JackPDouglas также предлагает использовать дизайн, основанный на наследовании таблиц классов . См. его пример или мои примеры подобной техники здесь или здесь или здесь .

1 голос
/ 04 декабря 2008

Если вы заинтересованы в моделировании данных, в дополнение к объектному моделированию, я предлагаю вам поискать "специализацию обобщения реляционного моделирования" в Интернете.

Раньше были хорошие ресурсы, которые достаточно хорошо объясняют этот тип паттернов.

Я надеюсь, что эти ресурсы все еще там.

Вот упрощенное представление о том, что, я надеюсь, вы найдете.

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

Одним из способов моделирования данных на концептуальном уровне является модель Entity-Relationship (ER). Существуют хорошо известные модели для моделирования ситуации специализации-обобщения. Преобразование этих шаблонов ER в таблицы SQL (называемое логическим дизайном) довольно просто, хотя вы должны сделать несколько вариантов проектирования.

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

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

0 голосов
/ 13 марта 2015

Возможно, я бы добавил ограничение проверки.
Создайте ForeignKeys как Nullable. Добавьте проверку, чтобы убедиться, что они не равны NULL, и чтобы они не были оба установлены. CONSTRAINT [CK_HasOneForiegnKey] CHECK ((FK_First! = NULL ИЛИ FK_Second! = NULL) И НЕ (FK_First! = NULL И FK_Second! = NULL)).

Я не уверен, но я верю, что это позволит вам устанавливать только одну клавишу за раз.

0 голосов
/ 02 декабря 2008

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

В зависимости от того, сколько интеллекта вы хотите поместить в свою схему (и сколько MS SQL Server позволяет вам в нее помещать), вам на самом деле не нужно будет объединять таблицы подклассов, поскольку вы знаете, что если Идентификатор существует в любой таблице подклассов, он должен существовать в том же подклассе, который указан в столбце кода подкласса.

0 голосов
/ 02 декабря 2008

Спасибо, Билл. Вы заставили меня задуматься ...

Таблица суперкласса имеет столбец кода подкласса. Каждая из таблиц подкласса имеет ограничение внешнего ключа, а также таблицу, которая диктует, что идентификатор существует с подмножеством таблицы суперкласса (где code = athlete).

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

Edit: Bleh, такая хорошая идея звучит ... Но мешает тот факт, что подзапросы, которые ссылаются на другие таблицы, не поддерживаются. По крайней мере, не в SQL Server.

0 голосов
/ 02 декабря 2008

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

Основная проблема - попытаться проверить, как он вставлен. Студент должен быть вставлен первым, чтобы вы не нарушали ограничение FK в подтаблице, чтобы триггер, выполняющий проверку, не работал.

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

У меня есть БД с таблицей в иерархии подклассов, как это также. Я использую Hibernate и его отображение правильно, поэтому он удаляет все автоматически. Если делать это «вручную», то я всегда буду удалять родителя с соответствующими каскадами хе-хе:)

...