Комментарии 20
чтобы у одного человека не могло быть одновременно больше одного неустаревшего купона.
Почему бы не написать "у одного человека должен быть максимум только один действующий купон"? Нелегко неверно истолковать непроработанные неверные смысловые конструкции, не так ли?
Хм, ваш триггер не даст воткнуть в БД валидный на дату выдачи, но просроченный купон для пользователя, который по бизнес-логике надо было завести прошлой датой, если у него есть текущий купон, выданный после устаревания вводимого. Для новых работать будет. А вот решение с gist этого недостатка лишено, если я правильно понимаю принцип его работы.
А можно узнать почему такой констрейнт необходимо сделать именно на стороне бд? Имхо, это бизнес логика и она должна решаться кодом. Можно в одной транзакции сразу получить количество валидных купонов и вставить новый купон только если количество равно 0, иначе закрыть транзакцию.
По сути - это вопрос о том, зачем вообще нужны констрейнты бд. Те же Unique, Foreign key и т.д.
Это просто дополнительная защита от случайных ошибок. Например, в одном месте при выдаче купонов учли момент с уникальностью, а в другом (при массовом импорте купонов ) - нет. И тогда будет хаос.
Разумеется, всегда есть неважные места, где можно ничего не проверять на базе. А есть работа с деньгами, где лучше 3 раза перебдеть.
Всё-таки я бы сказал, что стремиться надо к тому, чтобы констрейнты базы данных отвечали только за консистентность данных.
То есть, например, если запись в таблице A ссылается на запись в таблице B, то соответствующая запись в B должна существовать, - это хороший пример использования констрейнта.
Или не должно быть записей с одинаковым ключом, - это тоже хороший пример.
А ограничения бизнес-логики не надо тащить в базу. Поначалу это может выглядеть как хороший способ лишний раз подстелить соломки. А через пару лет выстрелит очень-очень больно.
Если есть необходимость валидировать бизнес-логику, и быть уверенным, что мы эту валидацию всюду делаем одинаково, то в некоторых случаях хорошим решением будет создать апи, которое единственное будет писать в базу. А все остальные части системы будут взаимодействовать с базой только через него.
Так вы можете писать валидацию бизнес-логики на языке, подходящем для написания бизнес-логики, а не через триггеры с констрейнтами.
на практике я не раз встречал ситуацию, когда отсутствие unique или foreign key приводили к проблемам. И наоборот: не вижу особой проблемы добавить exclude
В общем, это зависит от конкретики, я бы не обобщал и не делал бы из этого глобального правила
Не очень понимаю, как вы собираетесь обеспечивать "ограничения бизнес-логики" без базы: есть два (или три, или тридцать три) сервера, реализующих "апи", в итоге они могут понаделать сколько угодно невалидных купонов.
Как бы вы решали такую задачу? Предположим, есть таблица с купонами, и у купонов есть некая дата устаревания valid_until. Вам надо обеспечить такое ограничение (constraint) на уровне БД, чтобы у одного человека мог быть только один действующий купон.
Я бы, наверное, сразу объявил задачу недоопределённой. Вполне жизненная ситуация - у меня есть два купона, оба в будущем, причём пересекающиеся. Я хочу оба зарегистрировать, а впоследствии, но до начала их действия, от одного отказаться. В реальности всё проще - из всех актуальных берём наиболее выгодный, и дело с концом..
Нет, просто как контроль на отсутствие пересекающихся диапазонов - всё прекрасно. А вот сам пример для демонстрации методы - какой-то не очень... уж лучше бы придумать... ну я не знаю, дополнительная банка пива в самолёте - в двух-то одновременно ну никак не полететь.
Ещё изучаю Postgres. Подскажите а почему нельзя сделать UNIQUE с условием
WHERE (valid_until > created_at)
ведь в tsrange использован именно этот интервал?
в вашем случае и valid_until и created_at берутся у одной и той же записи. Т.е. логически это действует так: выбираются все строки, где valid_until > created_at, а потом среди них смотится, есть ли повторы юзеров. Т.е. бессмыслица, так как valid_until > created_at это true для всех записей.
Exclude же проверяет, есть ли другие записи, которые пересекаются по диапазону со вставляемой записью
Непонятно почему купон может быть только один.
Триггер некорректный - при параллельной вставке можно пропустить два купона.
PostgreSQL: обеспечение уникальности записи с проверкой даты валидности