Как стать автором
Обновить

Комментарии 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 же проверяет, есть ли другие записи, которые пересекаются по диапазону со вставляемой записью

Непонятно почему купон может быть только один.

Триггер некорректный - при параллельной вставке можно пропустить два купона.

спасибо, отметил в статье, что триггер надо дорабатывать

Зарегистрируйтесь на Хабре, чтобы оставить комментарий