Подумайте о том, как вы бы моделировать операции в ОО дизайн: операции будут подклассами общего суперкласса Operation
. Каждый подкласс должен иметь обязательные члены объекта для соответствующего оборудования, необходимого для этой операции.
Способ моделирования этого с помощью SQL - Class Table Inheritance. Создать общую супер-таблицу:
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
. Ограничьте их правильными типами, создав супер-таблицу наследования класса Class для оборудования.
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
не требует никакого оборудования, так что это не имеет EQUIP столбцы:
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)
);
Таблица 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 - использовать декларативные ограничения для моделирования ваших бизнес-правил. Очевидно, это требует, чтобы вы создавали новую подкатегорию каждый раз, когда вы создаете новый тип операции. Это лучше всего, когда операции и бизнес-правила никогда (или почти никогда) не меняются. Но это может не соответствовать вашим требованиям к проекту. Большинство людей говорят: «Но мне нужно решение, которое не требует изменений схемы».
Большинство разработчиков, вероятно, не выполняют Наследование классов. Чаще всего они используют структуру таблиц «один-ко-многим», как упомянули другие люди, и внедряют бизнес-правила исключительно в код приложения. То есть ваше приложение содержит код для вставки только оборудования, соответствующего каждому типу операции.
Проблема с использованием логики приложения заключается в том, что она может содержать ошибки и может вставлять данные, которые не соответствуют бизнес-правилам. Преимущество Наследования классов таблицы заключается в том, что с хорошо разработанными ограничениями РСУБД последовательно выполняет целостность данных. У вас есть уверенность, что база данных буквально не может хранить неправильные данные.
Но это также может быть ограничением, например, если ваши бизнес-правила меняются, и вам необходимо настроить данные. Общим решением в этом случае является запись сценария для выгрузки всех данных, изменения вашей схемы, а затем перезагрузки данных в форме, которая теперь разрешена (Extract, Transform, and Load = ETL).
Итак, вы должны решить: хотите ли вы кодировать это на уровне приложения или на уровне схемы базы данных? Есть законные причины использовать любую стратегию, но она будет сложной в любом случае.
Re: Вы, кажется, говорите об хранении выражений в виде строк в полях данных. Я рекомендую против делая это. База данных предназначена для хранения данных, а не кода. Вы можете использовать ограниченную логику в ограничениях или триггерах, но код принадлежит вашему приложению.
Если у вас слишком много операций для моделирования в отдельных таблицах, то моделируйте его в коде приложения. Хранение выражений в столбцах данных и ожидание использования SQL для оценки запросов было бы похожим на разработку приложения при интенсивном использовании eval()
.
Являются ли оборудование класса A/B/C/D оборудованием или конкретными предметами оборудования? т. е. будет ли таблица оборудования иметь ровно четыре записи или большое количество записей четырех различных типов? –
В таблице «Оборудование» будет много строк (около 30). –
Любите значок ResEdit! –