Необходимо реализовать ограничение записей в таблицу за текущую дату. Postgres

Рейтинг: 0Ответов: 3Опубликовано: 29.03.2023

Есть таблица в бд

CREATE TABLE fio (                  
id serial,                   
user_id BIGINT NOT NULL PRIMARY KEY,  
first_name TEXT NOT NULL,  
second_name TEXT NOT NULL,                   
age INT NOT NULL,  
date_in date DEFAULT CURRENT_DATE);

Заполняется таблица из бота в телеграме.

Запрос на заполнение - (

"INSERT INTO fio(user_id, first_name, second_name, age) VALUES (%s,%s,%s,%s)",
(chat_id, first_name, second_name, age))

То есть, бот собирает данные о пользователях, для регистрации.

Нужно реализовать ограничение на заполнение 100 пользователей в текущую дату, если число превышено, в бот должен вернуться ответ - "ошибка".

Подскажите, как это лучше сделать, подзапросом или с помощью триггера?

Необходимо самое простое решение. Большое спасибо!

UPD: Запрос найти удалось. Помогите вернуть ошибку, если запись не добавилась, надо как-то получить от БД ответ, что запись не прошла.

Запрос -

("INSERT INTO fio (user_id, first_name, second_name, age) 
   SELECT %s,%s,%s,%s WHERE EXISTS (SELECT COUNT(1) FROM fio 
   WHERE date_in = CURRENT_DATE HAVING COUNT(1) < 5);",
(chat_id, first_name, second_name, age)

Ответы

▲ 0Принят

Просится триггер на вставку данных, но:

  1. Вообще-то кажется, что задача какая-то неправильная - прибивать в БД ограничение на число записей на текущую дату.
  2. Не оставляет ощущение, что остается лазейка вставки большего числа записей задним числом (за вчера и т.п.)
  3. Вообще мне кажется ответ @Akina с констрейном лучше

Ну да ладно, тоже вариант:

CREATE FUNCTION check_today_count() RETURNS trigger AS $check_today_count$
    DECLARE
        today_exists    integer;
    BEGIN
        -- Получить число записей за текущую дату
        today_exists = count(id) FROM fio WHERE date_in = CURRENT_DATE;
        -- Выбросить исключение с ошибкой при превышении 100 записей
        IF today_exists >= 100 THEN
            RAISE EXCEPTION 'Error: records limit reached';
        END IF;
        RETURN NEW;
    END;
$check_today_count$ LANGUAGE plpgsql;

CREATE TRIGGER check_today_count BEFORE INSERT ON fio
    FOR EACH ROW EXECUTE PROCEDURE check_today_count();
▲ 2

Задача решается созданием пользовательской функции, которая используется в табличном CHECK CONSTRAINT.

DEMO

CREATE TABLE fio (                  
id serial,                   
user_id BIGINT NOT NULL PRIMARY KEY,  
first_name TEXT NOT NULL,  
second_name TEXT NOT NULL,                   
age INT NOT NULL,  
date_in date DEFAULT CURRENT_DATE);
CREATE FUNCTION amount_today(OUT amount INT)
AS $$ 
  SELECT COUNT(*) FROM fio WHERE date_in = CURRENT_DATE
$$ LANGUAGE SQL;
ALTER TABLE fio ADD CHECK (amount_today() < 3);
INSERT INTO fio(user_id, first_name, second_name, age) VALUES (1, 'Иван', 'Иванов', 30);
INSERT INTO fio(user_id, first_name, second_name, age) VALUES (8, 'Пётр', 'Петров', 40);
INSERT INTO fio(user_id, first_name, second_name, age) VALUES (23, 'Сидор', 'Сидоров', 25);
INSERT INTO fio(user_id, first_name, second_name, age) VALUES (41, 'Василий', 'Пупкин', 33);
ERROR:  new row for relation "fio" violates check constraint "fio_check"
DETAIL:  Failing row contains (4, 41, Василий, Пупкин, 33, 2023-03-29).
SELECT * FROM fio;
id user_id first_name second_name age date_in
1 1 Иван Иванов 30 2023-03-29
2 8 Пётр Петров 40 2023-03-29
3 23 Сидор Сидоров 25 2023-03-29

fiddle

▲ 0

Силами базы данных это просто не сделать. Вероятней всего даже вообще не сделать - без каких-то особых извращений.

Проще перед добавлением записи проверять число записей за текущую дату

SELECT COUNT(*) FROM fio WHERE date_in = now()::date

и уже получив количество принимать решение - добавлять новую запись или возвращать ошибку