0

我有一个用户操作的时间戳。以及用户有权执行操作的几个时间间隔。我需要检查此操作的时间戳是否在至少一个时间间隔内。

用户表:

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”范围内,因此“Bob”有执行操作的授权,并且应该是“是”在第一行,而不是“否”。

预期的输出是这样的:

#|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

它工作正常 - 结果是“真实的”。为什么?array_agg 有什么问题?

我必须使用 array_agg 因为我有几个时间间隔要比较。

我必须制作“假”时间间隔

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

来自一个时间戳,因为运算符 @> 不允许比较时间戳和时间戳间隔数组。如何比较一个日期是否在时间间隔数组的至少一个时间间隔内?

4

2 回答 2

1

@>PostgreSQL中有几个运算符:

  • tstzrange @> tstzrange测试第一个间隔是否包含第二个

  • anyarray @> anyarray测试第一个数组是否包含第二个数组的所有元素。

    在您的查询中,它将测试第二个数组中的每个间隔是否在第一个数组中存在相等的间隔。

有一种方法可以测试一个区间是否包含在区间数组的一个元素中:

someinterval <@ ANY (array_of_intervals)

但是没有直接的方法可以用运算符表达您的条件。

在没有聚合的情况下,将两个表连接起来@>并计算结果行。

于 2019-10-25T15:03:19.990 回答
1

由于所有三个日期都是标量,因此不需要 Postgres 范围检查,一个简单的 BETWEEN 操作就足够了。

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”。

于 2019-10-25T17:56:12.467 回答