Я пытаюсь добавить следующее ограничение проверки в таблицу raffle_participant:
ALTER TABLE raffle_participant ADD CONSTRAINT ck_raffle_participant_total_purchased_le_ticket_limit CHECK (tickets_purchased_in_raffle(raffle_id) <= get_ticket_limit(raffle_id))
Когда я выполняю указанную выше команду, я возвращаюсь:
ERROR: check constraint "ck_raffle_participant_total_purchased_le_ticket_limit" of relation "raffle_participant" is violated by some row
Мне кажется, что все строки в таблице raffle_participant удовлетворяют ограничению. Что я тут не понимаю? Почему ограничение проверки нарушается для существующих данных?
Использование PostgreSQL версии 13.6.
Редактировать: Я пробовал следующие запросы, чтобы выяснить, какие строки нарушают ограничение, и, похоже, ни один из них не делает этого.
development=# select (select sum(p.tickets_purchased) from raffle_participant p where p.raffle_id = 2) <= (select r.ticket_limit from raffle r where r.id = 2);
?column?
----------
t
(1 row)
development=# select (select sum(p.tickets_purchased) from raffle_participant p where p.raffle_id = 3) <= (select r.ticket_limit from raffle r where r.id = 3);
?column?
----------
t
(1 row)
development=# select (select sum(p.tickets_purchased) from raffle_participant p where p.raffle_id = 4) <= (select r.ticket_limit from raffle r where r.id = 4);
?column?
----------
t
(1 row)
| Столбец | Тип | Сопоставление | Обнуляемый | По умолчанию |
|---|---|---|---|---|
| я бы | целое число | ненулевой | генерируется всегда как личность | |
| название предмета | переменный характер(40) | ненулевой | ||
| item_value | большойинт | ненулевой | ||
| ticket_limit | целое число | ненулевой | ||
| Стоимость билета | большойинт | ненулевой | ||
| user_ticket_limit | целое число | ненулевой | ||
| закончилось_на | временная метка с часовым поясом | |||
| id_победителя | большойинт |
| я бы | название предмета | item_value | ticket_limit | Стоимость билета | user_ticket_limit | закончилось_на | id_победителя |
|---|---|---|---|---|---|---|---|
| 1 | вещь | 10 | 10 | 10 | 2 | 2022-04-27 17:46:01.271025+00 | |
| 2 | вещь | 10 | 10 | 10 | 10 | 2022-04-27 17:55:28.783744+00 | 151150118697959424 |
| 3 | вещь | 10 | 10 | 10 | 10 | 2022-04-27 20:32:13.588843+00 | 151150118697959424 |
| 4 | вещь | 10 | 10 | 10 | 10 |
| Столбец | Тип | Сопоставление | Обнуляемый | По умолчанию |
|---|---|---|---|---|
| raffle_id | целое число | ненулевой | ||
| Логин пользователя | большойинт | ненулевой | ||
| ticket_purchased | целое число | ненулевой |
| raffle_id | Логин пользователя | ticket_purchased |
|---|---|---|
| 2 | 151150118697959424 | 10 |
| 3 | 151150118697959424 | 10 |
| 4 | 151150118697959424 | 3 |
CREATE FUNCTION tickets_purchased_in_raffle (IN raffle_id raffle.id%TYPE)
RETURNS raffle_participant.tickets_purchased%TYPE AS
$$
SELECT SUM(p.tickets_purchased) FROM raffle_participant p WHERE p.raffle_id = raffle_id
$$
LANGUAGE SQL
CREATE FUNCTION get_ticket_limit (IN raffle_id raffle.id%TYPE)
RETURNS raffle.ticket_limit%TYPE AS
$$
SELECT r.ticket_limit FROM raffle r WHERE r.id = raffle_id
$$
LANGUAGE SQL
Что ты имеешь в виду? Является ли синтаксис этого контрольного ограничения недопустимым? Моя проблема в том, что я не могу добавить ограничение в таблицу с показанными данными. Глядя на данные, кажется, что нет нарушающих строк.
нет, когда postgres пытается добавить ограничение, он проверяет все строки, и у вас есть некоторые, которые нарушают это, поэтому есть по крайней мере одна строка, которая имеет (tickets_purchased_in_raffle(raffle_id) > get_ticket_limit(raffle_id)), поэтому сделайте SELECT и выберите это первый
К вашему сведению, то, что вы делаете, не является хорошей идеей. Отсюда Создать таблицу: "В настоящее время выражения CHECK не могут содержать подзапросы и ссылаться на переменные, отличные от столбцов текущей строки...". Обман с помощью функции — не способ обойти это. В CHECK нет отслеживания зависимостей, поэтому вполне возможно, что при переносе этого в новую базу данных произойдет сбой, потому что CHECK запускается до того, как будут загружены необходимые ему функции. Для такого рода вещей используйте триггер.
Я заметил, что вы не проверили raffle_id = 1. Лучше принять ошибку за истину и найти исключение из правила, которое приводит к сбою CHECK. Также помните sum(), где raffle_id не существует, это NULL, а NULL <= <some_number> будет NULL, а не «t» или «f».


Проблема в том, что мой входной аргумент raffle_id для tickets_purchased_in_raffle затенялся именем столбца raffle_id из raffle_participant. Изменив имя аргумента на rid, я смог успешно добавить контрольное ограничение.
Повторяю свое предупреждение, не делайте этого. Использование функции и/или обращение к строкам в другой таблице вызовет у вас проблемы. Для более подробного объяснения см. Проверить ограничения, в частности часть Примечание.
Отмеченный. Я рассмотрю возможность использования триггеров для этого. Спасибо
вот что не удалось:
(tickets_purchased_in_raffle(raffle_id) <= get_ticket_limit(raffle_id))