У меня есть 2 таблицы (ItemType и Item). ItemType имеет логическое значение hasSize
, а таблица Item имеет столбец size
.
Я хотел бы обеспечить, чтобы size
устанавливалось только в том случае, если логическое значение hasSize
= true.
Я видел обсуждаемые решения, которые используют триггеры для проверки. Однако это не обеспечивает такой же надежности, как ограничение.
Является ли это проблемой неидеального дизайна схемы или для этого есть подходящие решения?
Да, я бы назвал это проблемой дизайна. Обычно таких проблем можно избежать, сохраняя нетhassize
, поскольку его можно получить при запросе к базе данных.
Иногда необходимо хранить избыточные данные для повышения производительности, но тогда приходится расплачиваться потенциальной несогласованностью, которой вы опасаетесь.
Если вы не можете избежать сохранения hassize
, использование триггера обычно является лучшим решением; старайтесь избегать аномалий, уменьшая параллелизм (например, блокировки или сериализуемую изоляцию).
Правильно, тогда вы можете запросить тип элемента и определить таким образом, установлен ли размер.
Ограничения проверки не могут ссылаться на другие таблицы в базе данных, и триггер является допустимой стратегией обхода этого ограничения. Я не думаю, что ваш дизайн схемы плохой. Некоторые типы предметов имеют большие размеры, и эта информация принадлежит таблице item_type
.
Вот альтернативное решение, не использующее триггеры, где я накладываю проверочное ограничение на ОБНОВЛЯЕМЫЙ ВИД, чтобы предотвратить изменение данных в базовых таблицах, используя модификатор WITH CHECK OPTION
. обратите внимание, что это будет проверяться только в том случае, если данные вставлены или обновлены через представление. Вы должны предоставить соответствующие разрешения, чтобы роль приложения не могла напрямую изменять таблицу item
, но могла через UPDATABLE VIEW
Вот автономный скрипт с примерами данных и примерами вставки, которая не удалась, и вставки, которая прошла успешно.
-- set up dummy `item_type` table
CREATE TABLE item_type (
id int primary key generated by default as identity,
has_size bool,
name text not null unique
);
INSERT INTO item_type
(has_size, name)
VALUES
(true, 'sizable type')
, (false, 'unisize');
-- set up dummy `item` table
CREATE TABLE item (
id int primary key generated by default as identity,
type_id int references item_type(id),
name text NOT NULL,
size text
);
INSERT INTO item
(type_id, name, size)
VALUES
(1, 'worlds best dad tee shirt', 'M')
, (2, 'usb key', NULL);
-- create updatable check view.
CREATE VIEW item_view AS
SELECT item.*
FROM item
WHERE EXISTS (
SELECT
FROM item_type
WHERE item.type_id = item_type.id
AND (item.size IS NOT NULL) = item_type.has_size
)
WITH CASCADED CHECK OPTION;
-- ^^^
-- i could have alternately used local
-- instead of cascaded
Эта вставка не удастся
INSERT INTO item_view
(type_id, name, size)
VALUES
(2, 'dad socks', 'M');
Выдает следующую ошибку:
ERROR: new row violates check option for view "item_view"
DETAIL: Failing row contains (3, 2, dad socks, M).
Эта вставка получится
INSERT INTO item_view
(type_id, name, size)
VALUES
(2, 'dad socks', NULL);
SELECT * FROM item
возвращает следующий результат, как и ожидалось
id | type_id | name | size
----+---------+---------------------------+------
1 | 1 | worlds best dad tee shirt | M
2 | 2 | usb key |
4 | 2 | dad socks |
Спасибо за подробный ответ, похоже, обновляемое представление определенно приблизится к идеальному сценарию. Единственным недостатком является то, что создание представления не будет ретроспективно проверять какие-либо строки, которые нарушают ограничение проверки. Таким образом, будут проверяться только новые INSERT и UPDATE (аналогично триггеру).
@ J3Y, вы правы в отношении недостатка, однако вы можете отменить предикаты в запросе создания представления, чтобы пометить все строки, которые не соответствуют ограничению, и исправить их перед переносом базы данных для использования обновляемого представления. Я должен добавить, что если вы применяете ограничения, они должны быть двунаправленными, то есть вы также должны настроить обновляемое представление для item_type
, которое предотвратит обновления поля has_size
, если есть связанные item
s
Что вы подразумеваете под выводом
hasSize
при запросе к базе данных? Мое первоначальное намерение состояло в том, чтобы ItemType (например, одежда) требовал, чтобы у связанных элементов был установлен столбец размера.