【问题标题】:How correctly test the date is within time intervals在时间间隔内测试日期的正确程度
【发布时间】:2019-10-25 13:36:23
【问题描述】:

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

用户表:

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, '[]'))

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

【问题讨论】:

    标签: postgresql datetime array-agg


    【解决方案1】:

    PostgreSQL 中有几个@&gt; 运算符:

    • tstzrange @&gt; tstzrange 测试第一个区间是否包含第二个区间

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

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

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

    someinterval <@ ANY (array_of_intervals)
    

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

    不使用聚合,连接@&gt; 上的两个表并计算结果行数。

    【讨论】:

    • 非常感谢。计算时间戳在时间间隔内的次数非常好。
    【解决方案2】:

    由于所有三个日期都是标量,因此不需要 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”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-11
      • 2019-05-13
      • 1970-01-01
      • 2018-07-11
      • 2017-10-27
      • 1970-01-01
      • 2017-12-29
      相关资源
      最近更新 更多