需要获取日期时间范围之间的用户数

时间:2018-06-20 16:13:02

标签: sql

我需要创建一个报告,该报告显示在用户提供的时间范围内登录的用户数。因为我是新手,这对我来说似乎有点复杂。 我们需要计算所选时间范围内小时之间登录的用户

id,startdate,           enddate
1  01012018:14:01:10   01012018:15:30:40
1  01012018:16:11:50   01012018:16:30:45
2  01012018:09:41:50   01012018:16:30:45
1  01012018:09:41:50   01012018:10:30:45
2  01012018:19:41:50   01012018:21:30:45
3  01012018:09:31:10   01012018:21:20:45

预期输出:

startdate enddate  hours     total users logged
01012018  01012018 09-10
01012018  01012018 10-11
01012018  01012018 11-12
01012018  01012018 13-14 
01012018  01012018 14-15         01
01012018  01012018 15-16         01
01012018  01012018 16-17         01
01012018  01012018 17-18         01
01012018  01012018 18-19         01
01012018  01012018 19-20         01
01012018  01012018 20-21         01
01012018  01012018 21-22         01
01012018  01012018 22-23         
01012018  01012018 23-00  

1 个答案:

答案 0 :(得分:0)

您似乎将日期和时间存储为文本。这意味着您不能使用Postgres的内置日期函数进行比较。而且您已经以难以使用的格式存储了它们,“ DDMMYYYY”或“ MMDDYYYY”是模棱两可的,需要对其进行解析才能对其进行排序。除非您有充分的理由,否则请坚持使用ISO 8601作为日期。一切都能理解,而且排序很容易。

您的表应如下所示。

create table account_logins (
    id          serial primary key,
    account     integer not null references accounts(id),
    start_at    timestamp not null,
    end_at      timestamp not null
);

每个条目都有自己的ID,这是一种很好的做法。该帐户被声明为正确的外键。它使用的是timestamp type,这意味着我们可以使用all the Postgres date functions。最后,我将它们命名为start_atend_at,因为它们是时间戳,而不是日期。 foo_at是许多系统用于时间戳的命名约定。

这里有足够的数据可以对其进行测试。

 id | account |      start_at      |       end_at       
----+---------+---------------------+---------------------
  1 |       1 | 2018-01-01 14:01:10 | 2018-01-01 15:30:40
  2 |       1 | 2018-01-01 16:11:50 | 2018-01-01 16:30:45
  3 |       2 | 2018-01-01 16:13:45 | 2018-01-01 16:25:11

我们想要的是这样的东西。

hour  | num_logins
------+-----------
...   | 0
13:00 | 0
14:00 | 1
15:00 | 1
16:00 | 2
17:00 | 0
...   | 0

现在我们的架构处于更好的状态,我们可以开始构建查询。首先,我们需要从上午9点到午夜的所有时间。我们可以使用generate_series来做到这一点。

select hour
from generate_series(
    '20180101 09:00'::timestamp,
    '20180101 23:00'::timestamp,
    '1 hour'
) as series(hour);

这为我们提供了工作时间列表。

        hour         
---------------------
 2018-01-01 09:00:00
 2018-01-01 10:00:00
 2018-01-01 11:00:00
 2018-01-01 12:00:00
 2018-01-01 13:00:00
 2018-01-01 14:00:00
 2018-01-01 15:00:00
 2018-01-01 16:00:00
 2018-01-01 17:00:00
 2018-01-01 18:00:00
 2018-01-01 19:00:00
 2018-01-01 20:00:00
 2018-01-01 21:00:00
 2018-01-01 22:00:00
 2018-01-01 23:00:00

as series(hour)为生成的表命名series,并为列hour命名。这将使简短参考变得更容易。

现在,我们通过检查哪些登录名在account_logins ... hour范围内,来与hour+1一起工作。

select hour, id
from generate_series(
    '20180101 09:00'::timestamp,
    '20180101 23:00'::timestamp,
    '1 hour'
) as series(hour)
left outer join account_logins
    on start_at < hour+'1 hour' and hour < end_at;

left outer join确保series中的每个小时都被选中,即使该小时没有登录。请参阅Visual Representation of SQL Joins,以了解各种连接。

这给了我们这个。

        hour         | id 
---------------------+----
 2018-01-01 09:00:00 |   
 2018-01-01 10:00:00 |   
 2018-01-01 11:00:00 |   
 2018-01-01 12:00:00 |   
 2018-01-01 13:00:00 |   
 2018-01-01 14:00:00 |  1
 2018-01-01 15:00:00 |  1
 2018-01-01 16:00:00 |  2
 2018-01-01 16:00:00 |  3
 2018-01-01 17:00:00 |   
 2018-01-01 18:00:00 |   
 2018-01-01 19:00:00 |   
 2018-01-01 20:00:00 |   
 2018-01-01 21:00:00 |   
 2018-01-01 22:00:00 |   
 2018-01-01 23:00:00 |   

我们可以看到首次登录是在整个两个小时内都计算在内的,而16:00有两次登录。

最后一步是将小时数归为group by hour,计算每小时的登录次数count(id),并确保以正确的顺序order by hour进行登录

select hour, count(id) as "total users logged"
from generate_series(
    '20180101 09:00'::timestamp,
    '20180101 23:00'::timestamp,
    '1 hour'
) as series(hour)
left outer join account_logins
    on start_at < hour+'1 hour' and hour < end_at
group by hour
order by hour;

在那里,你有它。

        hour         | total users logged 
---------------------+--------------------
 2018-01-01 09:00:00 |                  0
 2018-01-01 10:00:00 |                  0
 2018-01-01 11:00:00 |                  0
 2018-01-01 12:00:00 |                  0
 2018-01-01 13:00:00 |                  0
 2018-01-01 14:00:00 |                  1
 2018-01-01 15:00:00 |                  1
 2018-01-01 16:00:00 |                  2
 2018-01-01 17:00:00 |                  0
 2018-01-01 18:00:00 |                  0
 2018-01-01 19:00:00 |                  0
 2018-01-01 20:00:00 |                  0
 2018-01-01 21:00:00 |                  0
 2018-01-01 22:00:00 |                  0
 2018-01-01 23:00:00 |                  0

您可以使用various date functions来获取所需的格式,但是我建议保持查询简单和通用。取而代之的是,让所有正在消耗并显示此数据格式的东西成为可能。将格式与功能分开。