Как правильно проверить дату в пределах временных интервалов - PullRequest
0 голосов
/ 25 октября 2019

У меня есть временная метка действия пользователя. И несколько временных интервалов, когда у пользователя есть права на выполнение действия. Мне нужно проверить, находится ли временная метка этого действия хотя бы в одном из временных интервалов или нет.

Таблица с пользователями:

CREATE TABLE ausers (
    id serial PRIMARY KEY,
    user_name VARCHAR(255) default NULL,
    action_date TIMESTAMP
);
INSERT INTO ausers VALUES(1,'Jhon', '2018-02-21 15:05:06');
INSERT INTO ausers VALUES(2,'Bob', '2018-05-24 12:22:26');

#|id|user_name|action_date
----------------------------------
1|1 |Jhon     |21.02.2018 15:05:06
2|2 |Bob      |24.05.2018 12:22:26

Таблица с грантами:

CREATE TABLE user_grants (
    id serial PRIMARY KEY,
    user_id INTEGER,
    start_date TIMESTAMP,
    end_date TIMESTAMP
);
INSERT INTO user_grants VALUES(1, 1, '2018-01-01 00:00:01', '2018-03-01 00:00:00');
INSERT INTO user_grants VALUES(2, 1, '2018-06-01 00:00:01', '2018-09-01 00:00:00');
INSERT INTO user_grants VALUES(3, 2, '2018-01-01 00:00:01', '2018-02-01 00:00:00');
INSERT INTO user_grants VALUES(4, 2, '2018-02-01 00:00:01', '2018-03-01 00:00:00');

#|id|user_id|start_date           |end_date
------------------------------------------------------
1|1 |1      |01.01.2018 00:00:01  |01.03.2018 00:00:00
2|2 |1      |01.06.2018 00:00:01  |01.09.2018 00:00:00
3|3 |2      |01.01.2018 00:00:01  |01.02.2018 00:00:00
4|4 |2      |01.02.2018 00:00:01  |01.03.2018 00:00:00

Запрос:

select u.user_name,
case 
    when array_agg(gr.range) @> array_agg(tstzrange(u.action_date, u.action_date, '[]')) then 'Yes'
    else 'No' 
end as "permition was granted"
from ausers u
left join (select tstzrange(ug.start_date, ug.end_date, '[]') as range, ug.user_id as uid 
from user_grants ug) as gr on gr.uid = u.id
group by u.user_name;

Результат:

#|user_name|permition was granted
---------------------------------
1|Bob      |No
2|Jhon     |No

Метка времени '01 .02.2018 15:05:06 'находится внутри "01.01.2018 00:00:01,01.03.2018 00:00:00 », поэтому у« Боба »были права на выполнение действия, и где в первой строке должно быть« Да », а не« Нет ».

Ожидаемый результат будет следующим:

#|user_name|permition was granted
---------------------------------
1|Bob      |Yes
2|Jhon     |No

Я пытался проверить, как это:

select array_agg(tstzrange('2018-02-21 15:05:06', '2018-02-21 15:05:06', '[]')) <@ array_agg(tstzrange('2018-01-01 00:00:01', '2018-03-01 00:00:01', '[]'));

#|?column?
----------
 |false

Результат "ложь". Но если удалить функцию array_agg

select tstzrange('2018-02-21 15:05:06', '2018-02-21 15:05:06', '[]') <@ tstzrange('2018-01-01 00:00:01', '2018-03-01 00:00:01', '[]');

#|?column?
----------
 |true

Работает нормально - результат "true". Почему? Что не так с array_agg?

Я должен использовать array_agg, потому что у меня есть несколько временных интервалов для сравнения.

Мне нужно сделать "поддельный" временной интервал

array_agg(tstzrange(u.action_date, u.action_date, '[]'))

изодна временная метка, потому что оператор @> не позволяет сравнивать временную метку и массив интервалов временных меток. Как сравнить эту дату с интервалом времени, по крайней мере, на временном интервале?

Ответы [ 2 ]

1 голос
/ 25 октября 2019

Поскольку все три даты являются скалярными величинами, проверка диапазона Постгреса не требуется, достаточно простой операции МЕЖДУ.

select au.user_name
     , case when ug.user_id is null then 'No' else 'Yes' end authorized 
  from ausers au
  left join user_grants ug   
         on (    au.id = ug.id
             and au.action_date between ug.start_date and ug.end_date
            );  

Кстати. Я думаю, что ваши ожидаемые результаты опубликованы в обратном направлении. Ни одно из имен пользователей не имеет отметки времени '01 .02.2018 15:05:06 ', как указано в описании.

1 голос
/ 25 октября 2019

В PostgreSQL есть несколько @> операторов:

  • tstzrange @> tstzrange проверяет, содержит ли первый интервал второй

  • anyarray @> anyarray проверяет, содержит ли первый массив все элементы второго массива.

    В вашем запросе будет проверяться, есть ли для каждого интервала во втором массиве интервал равный в первом массиве.

Тем самым можно проверить, содержится ли интервал в одном из элементов массива интервалов:

someinterval <@ ANY (array_of_intervals)

, но не существует простого способа выразитьваше условие с оператором.

Обойдитесь без агрегата, объедините две таблицы на @> и посчитайте строки результатов.

...