Подумайте о том, как вы смоделируете операции в ОО-дизайне: операции были бы подклассом общего суперкласса Operation
.Каждый подкласс будет иметь обязательные члены объекта для соответствующего оборудования, требуемого для этой операции.
Способ моделирования этого с помощью SQL - Наследование таблиц классов .Создайте общую супер-таблицу:
CREATE TABLE Operation (
operation_id SERIAL PRIMARY KEY,
operation_type CHAR(1) NOT NULL,
UNIQUE KEY (operation_id, operation_type),
FOREIGN KEY (operation_type) REFERENCES OperationTypes(operation_type)
);
Затем для каждого типа операции определите подтаблицу со столбцом для каждого необходимого типа оборудования.Например, OperationFoo
имеет столбец для каждого из equipA
и equipB
.Поскольку оба они обязательны, столбцы NOT NULL
.Укрепите их до правильных типов, создав супер-таблицу наследования таблиц классов для оборудования.
CREATE TABLE OperationFoo (
operation_id INT PRIMARY KEY,
operation_type CHAR(1) NOT NULL CHECK (operation_type = 'F'),
equipA INT NOT NULL,
equipB INT NOT NULL,
FOREIGN KEY (operation_id, operation_type)
REFERENCES Operations(operation_d, operation_type),
FOREIGN KEY (equipA) REFERENCES EquipmentA(equip_id),
FOREIGN KEY (equipB) REFERENCES EquipmentB(equip_id)
);
Для таблицы OperationBar
не требуется никакого оборудования, поэтому в ней нет оборудованных столбцов:
CREATE TABLE OperationBar (
operation_id INT PRIMARY KEY,
operation_type CHAR(1) NOT NULL CHECK (operation_type = 'B'),
FOREIGN KEY (operation_id, operation_type)
REFERENCES Operations(operation_d, operation_type)
);
Table OperationBaz имеет одно необходимое оборудование equipA
, и тогда как минимум одно из equipB
и equipC
должно быть NOT NULL
.Для этого используйте ограничение CHECK
:
CREATE TABLE OperationBaz (
operation_id INT PRIMARY KEY,
operation_type CHAR(1) NOT NULL CHECK (operation_type = 'Z'),
equipA INT NOT NULL,
equipB INT,
equipC INT,
FOREIGN KEY (operation_id, operation_type)
REFERENCES Operations(operation_d, operation_type)
FOREIGN KEY (equipA) REFERENCES EquipmentA(equip_id),
FOREIGN KEY (equipB) REFERENCES EquipmentB(equip_id),
FOREIGN KEY (equipC) REFERENCES EquipmentC(equip_id),
CHECK (COALESCE(equipB, equipC) IS NOT NULL)
);
Аналогично в таблице OperationQuux
вы можете использовать ограничение CHECK
, чтобы убедиться, что хотя бы один ресурс оборудования в каждой паре не равен нулю:
CREATE TABLE OperationQuux (
operation_id INT PRIMARY KEY,
operation_type CHAR(1) NOT NULL CHECK (operation_type = 'Q'),
equipA INT,
equipB INT,
equipC INT,
equipD INT,
FOREIGN KEY (operation_id, operation_type)
REFERENCES Operations(operation_d, operation_type),
FOREIGN KEY (equipA) REFERENCES EquipmentA(equip_id),
FOREIGN KEY (equipB) REFERENCES EquipmentB(equip_id),
FOREIGN KEY (equipC) REFERENCES EquipmentC(equip_id),
FOREIGN KEY (equipD) REFERENCES EquipmentD(equip_id),
CHECK (COALESCE(equipA, equipB) IS NOT NULL AND COALESCE(equipC, equipD) IS NOT NULL)
);
Это может показаться большой работой.Но вы спросили, как это сделать в SQL.Лучший способ сделать это в SQL - использовать декларативные ограничения для моделирования ваших бизнес-правил.Очевидно, что для этого требуется, чтобы вы создавали новую вложенную таблицу каждый раз, когда создаете новый тип операции.Это лучше всего, когда операции и бизнес-правила никогда (или почти никогда) не меняются.Но это может не соответствовать требованиям вашего проекта.Большинство людей говорят: «Но мне нужно решение, которое не требует изменений схемы».
Большинство разработчиков, вероятно, не используют наследование таблиц классов.Чаще всего они просто используют структуру таблиц «один ко многим», как упоминали другие, и реализуют бизнес-правила исключительно в коде приложения.То есть ваше приложение содержит код для вставки только оборудования, соответствующего каждому типу операции.
Проблема, связанная с логикой приложения, заключается в том, что оно может содержать ошибки и может вставлять данные, которые не соответствуют бизнес-правилам.Преимущество наследования таблиц классов заключается в том, что при правильно спроектированных ограничениях СУБД последовательно обеспечивает целостность данных.Вы уверены, что база данных буквально не может хранить неверные данные.
Но это также может быть ограничением, например, если изменяются ваши бизнес-правила и вам необходимо скорректировать данные.Распространенным решением в этом случае является написание сценария для выгрузки всех данных, изменения схемы и перезагрузки данных в допустимой форме ( Извлечение, преобразование и загрузка = ETL )..
Итак, вы должны решить: хотите ли вы закодировать это на уровне приложения или на уровне схемы базы данных?Существуют обоснованные причины для использования любой из этих стратегий, но в любом случае она будет сложной.
Ваш комментарий: Вы, похоже, говорите о хранении выражений в виде строк в полях данных.Я рекомендую против делать это.База данных предназначена для хранения данных, а не кода.Вы можете использовать некоторую ограниченную логику в ограничениях или триггерах, но код принадлежит вашему приложению.
Если у вас слишком много операций для моделирования в отдельных таблицах, то моделируйте его в коде приложения.Хранить выражения в столбцах данных и ожидать, что SQL будет использовать их для оценки запросов, будет похоже на разработку приложения с интенсивным использованием eval()
.