Да, это возможно.
Это один из способов (на мой взгляд, наилучший способ), в которых работают каркасы объектно-реляционного сопоставления (ORM). И это не случайно, потому что вы пытаетесь реализовать здесь объектно-ориентированную иерархию наследования, отображенную в реляционную базу данных. «Устройство» является базовым классом, а «Лампа» и «Нагреватель» являются производными (потомками) классами.
Вот как это сделать:
Избавиться от Type
столбец. Как я покажу ниже, он станет вычисляемым столбцом.
Вместо столбца Type
введите пару так называемых столбцов "id-потомка": столбец HeaterId
и столбец LampId
. Добавьте ограничение, если вы sh, что только один из этих столбцов с идентификатором потомка может быть ненулевым. И если вы добавите больше устройств позже, вам придется добавить больше столбцов с идентификатором потомка.
Затем вы можете сделать Type
вычисляемым столбцом, на основе которого один из столбцов с идентификатором потомка содержит ненулевое значение.
Плохая новость заключается в том, что вы не сможете установить значение столбца Type
, но это нормально; вместо установки Type
вы будете неявно определять тип, сохраняя ненулевое значение в одном из столбцов с идентификатором потомка.
Примечание: в объектно-ориентированном программировании очень редко проектируют базовый класс, чтобы иметь какие-либо знания о своих потомках. Некоторые могут даже сказать, что иметь такие знания нелепо. Я не был бы таким абсолютным, я сталкивался с примерами небольших, тесно связанных, нерасширяемых графов классов, где такие знания являются законными и полезными для получения. Но в любом случае, даже если вы строго придерживаетесь принципа, который говорит, что базовый класс никогда не должен ничего знать о своих потомках, это не совсем объектно-ориентированное программирование: это реляционная база данных. И это цена, которую вы должны заплатить, чтобы отобразить граф классов в базу данных.
Дополнительные пояснения:
Во-первых, некоторая терминология: «длинный ряд» - это пара родителей (основание / предок) строка плюс нисходящая строка, исследованная вместе. «Фрагмент строки» - это только один из рассматриваемых отдельно.
Каждый фрагмент строки-потомка должен иметь точно такой же идентификатор первичного ключа, что и его родительский фрагмент строки. Итак, вот пример структуры:
Device table:
+-----+----------+----------+
| Id | LampId | HeaterId |
+-----+----------+----------+
| 1 | NULL | 1 |
| 2 | 2 | NULL |
| 3 | 3 | NULL |
| 4 | NULL | 4 |
+-----+----------+----------+
Lamp table:
+-----+----
| Id |
+-----+----
| 2 |
| 3 |
+-----+----
Heater table:
+-----+----
| Id |
+-----+----
| 1 |
| 4 |
+-----+----
Это означает, что во фрагменте базовой строки один идентификатор потомка, который не равен нулю, всегда имеет то же значение, что и первичный ключ этого та же строка.
Этот способ, помимо простоты устранения неполадок, имеет дополнительное преимущество в производительности: вставка всей длинной строки в базу данных не требует каких-либо дополнительных обращений к базе данных: вы используете последовательность или значение автоинкремента для выдачи только одного идентификатора, и это единственный идентификатор, который вам понадобится для всех фрагментов строки, участвующих в вставке. Это работает как очарование, даже если ваша иерархия наследования глубже, чем просто два уровня.