У меня есть таблица с этим макетом:
CREATE TABLE Favorites
(
FavoriteId uuid NOT NULL PRIMARY KEY,
UserId uuid NOT NULL,
RecipeId uuid NOT NULL,
MenuId uuid
)
Я хочу создать уникальное ограничение, подобное этому:
ALTER TABLE Favorites
ADD CONSTRAINT Favorites_UniqueFavorite UNIQUE(UserId, MenuId, RecipeId);
Тем не менее, это позволит несколько строк с одинаковыми (UserId, RecipeId)
, если MenuId IS NULL
. Я хочу , чтобы NULL
в MenuId
хранить любимую , который не соответствующее меню, но я хочу только более одного из этих строк на пользователя / рецепт пары.
У меня есть следующие идеи:
Используйте некоторые жестко закодированные UUID (например, все нули) вместо нуля.
ТемMenuId
не менее, имеет ограничения FK для меню каждого пользователя, поэтому мне пришлось бы создать специальное «нулевое» меню для каждого пользователя, что создает трудности.Проверьте наличие нулевой записи, используя вместо этого триггер.
Я думаю, что это хлопот, и мне нравится избегать триггеров, где это возможно. Кроме того, я не доверяю им, чтобы гарантировать, что мои данные никогда не будут в плохом состоянии.Просто забудьте об этом и проверьте наличие ранее нулевой записи в промежуточном программном обеспечении или в функции вставки, и у вас нет этого ограничения.
Я использую Postgres 9.0.
Есть ли какой-то метод, который я пропускаю?
источник
UserId
,RecipeId
), еслиMenuId IS NULL
?Ответы:
Создайте два частичных индекса :
Таким образом, может быть только одна комбинация,
(user_id, recipe_id)
гдеmenu_id IS NULL
эффективно реализуется желаемое ограничение.Возможные недостатки: у вас не может быть ссылки на внешний ключ
(user_id, menu_id, recipe_id)
, вы не можете основыватьсяCLUSTER
на частичном индексе, а запросы без соответствующегоWHERE
условия не могут использовать частичный индекс. (Кажется маловероятным, что вы захотите, чтобы FK ссылался на три столбца - используйте вместо этого столбец PK).Если вам нужен полный индекс, вы можете также отказаться от
WHERE
условия,favo_3col_uni_idx
и ваши требования по-прежнему выполняются.Индекс, теперь включающий всю таблицу, перекрывается с другим и становится больше. В зависимости от типичных запросов и процента
NULL
значений это может быть или не быть полезным. В экстремальных ситуациях это может даже помочь сохранить все три индекса (два частичных и итоговый сверху).Помимо: я советую не использовать смешанные идентификаторы в PostgreSQL .
источник
Вы можете создать уникальный индекс с объединением на MenuId:
Вам просто нужно выбрать UUID для COALESCE, который никогда не произойдет в «реальной жизни». Вы, вероятно, никогда не увидите нулевой UUID в реальной жизни, но вы можете добавить ограничение CHECK, если вы параноик (и так как они действительно хотят вас ...):
источник
(MenuId <> '00000000-0000-0000-0000-000000000000')
хотя.NULL
по умолчанию разрешено Кстати, есть три типа людей. Параноидальные, и люди, которые не делают базы данных. Третий вид время от времени отправляет вопросы о ТА в недоумении. ;)Вы можете хранить избранное без связанного меню в отдельной таблице:
источник
FavoriteWithoutMenu
первую очередь. Если это так, я просто добавляю ссылку в меню - в противном случае яFavoriteWithoutMenu
сначала создаю строку, а затем при необходимости связываю ее с меню. Это также затрудняет выбор всех избранных в одном запросе: мне нужно сделать что-то странное, например сначала выбрать все ссылки в меню, а затем выбрать все избранные, идентификаторы которых не существуют в первом запросе. Я не уверен, нравится ли мне это.NULL MenuId
, вы вставляете в эту таблицу. Если нет, кFavorites
столу. Но запросить, да, это будет сложнее.Я думаю, что здесь есть семантическая проблема. На мой взгляд, пользователь может иметь (но только один ) любимый рецепт для приготовления определенного меню. (У OP есть меню и рецепт, перепутанные; если я не прав: пожалуйста, поменяйте местами MenuId и RecipeId ниже) Это означает, что {user, menu} должен быть уникальным ключом в этой таблице. И это должно указывать ровно на один рецепт. Если у пользователя нет любимого рецепта для этого конкретного меню, для этой пары клавиш {user, menu} не должно быть строк . Также: суррогатный ключ (FaVouRiteId) является излишним: составные первичные ключи идеально подходят для таблиц реляционного отображения.
Это привело бы к сокращению определения таблицы:
источник