我需要创建一个报告,该报告显示在用户提供的时间范围内登录的用户数。因为我是新手,这对我来说似乎有点复杂。 我们需要计算所选时间范围内小时之间登录的用户
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
答案 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_at
和end_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来获取所需的格式,但是我建议保持查询简单和通用。取而代之的是,让所有正在消耗并显示此数据格式的东西成为可能。将格式与功能分开。