简体   繁体   English

sql查询分别每小时计数人

[英]sql query to count person in each hour separately

I have a table which has theses fields: 我有一个包含这些字段的表:

id | person_id | start_time | end_time | status

I want to get number of people which exists in each hour in a specific day, something like below pseudo code: 我想获取特定日期每一小时中存在的人数,例如下面的伪代码:

select count(person) from table where dow of end_time=day and end_time >= hour and start_time < hour+1 for hour in working hours of organization and day is a certain day of week

If it would be possible to have a temporary table which consists of hours of a day may be the following solution is a solution: 如果可能有一个由一天中的几个小时组成的临时表,则可以采用以下解决方案:

select t.h, count(s.id)
from
session s cross join (temperoray table with one column of hours in a day as t)
where
s.start_time < (t.h + 1) and s.end_time > t.h
group by
t.h

But I do not know a command which could create a temporary table as I needed. 但是我不知道可以根据需要创建临时表的命令。

I found this question which is very similar to what I want but all of its solution is based on group by which I do not think makes any sense in my case because parts of each group has common items, eg, a person could count as person in hour 11 and person in hour 12 and 13. 我发现这个问题与我想要的问题非常相似,但是它的所有解决方案都是基于group by ,在我的情况下,我认为这没有任何意义,因为每个组的各个部分都有共同的项目,例如,一个人可以算作一个人在第11小时,在第12和13小时的人。

I hope that I can find a way that gets me a table like this: 我希望我能找到一种使我得到如下表格的方式:

hour     |number of persons
10       |2
11       |0
12       |3
13       |1
...

Notice that it may possible to have some day with zero number of persons. 请注意,某天的人数可能为零。

Example: 例:

  id  | status |            start_time            |             end_time             | branch_id | person_id | session_type 
------+--------+----------------------------------+----------------------------------+-----------+-----------+--------------
 2675 | FI     | 2018-04-23 10:30:50.939693+04:30 | 2018-04-23 12:31:39.340692+04:30 |         1 |      1085 | IN
 2676 | FI     | 2018-04-23 11:47:06.683374+04:30 | 2018-04-23 13:23:52.659714+04:30 |         1 |      2722 | IN
 2677 | FI     | 2018-04-23 11:47:59.341765+04:30 | 2018-04-23 13:25:46.339266+04:30 |         1 |      2721 | IN
 2678 | FI     | 2018-04-23 11:58:34.854222+04:30 | 2018-04-23 13:25:55.08795+04:30  |         1 |      2723 | IN
 2679 | FI     | 2018-04-23 12:27:58.817234+04:30 | 2018-04-23 13:12:28.278699+04:30 |         1 |      2724 | IN
 2680 | FI     | 2018-04-23 12:30:36.552407+04:30 | 2018-04-23 12:30:54.088159+04:30 |         1 |      2725 | IN
 2681 | FI     | 2018-04-23 14:55:50.886725+04:30 | 2018-04-23 16:08:27.076629+04:30 |         1 |        25 | IN
 2682 | FI     | 2018-04-23 15:06:30.443347+04:30 | 2018-04-23 15:52:20.128546+04:30 |         1 |      2653 | IN
 2683 | FI     | 2018-04-23 15:21:57.979387+04:30 | 2018-04-23 16:16:09.289267+04:30 |         1 |      2580 | IN
 2684 | FI     | 2018-04-23 15:26:18.057999+04:30 | 2018-04-23 16:02:44.704133+04:30 |         1 |      2726 | IN
 2685 | FI     | 2018-04-23 16:50:10.2957+04:30   | 2018-04-23 17:23:01.732404+04:30 |         1 |      2727 | IN
 2686 | FI     | 2018-04-23 16:52:28.474299+04:30 | 2018-04-23 17:23:51.013318+04:30 |         1 |      2728 | IN
 2687 | FI     | 2018-04-23 16:58:05.796563+04:30 | 2018-04-23 17:33:03.259335+04:30 |         1 |      1646 | IN
 2688 | FI     | 2018-04-23 17:50:02.738009+04:30 | 2018-04-23 18:43:27.152203+04:30 |         1 |      2729 | IN
 2689 | FI     | 2018-04-23 18:47:12.19468+04:30  | 2018-04-23 19:25:46.606731+04:30 |         1 |      2730 | IN
 2690 | FI     | 2018-04-23 19:18:32.922065+04:30 | 2018-04-23 20:11:26.703693+04:30 |         1 |      2408 | IN
 2691 | FI     | 2018-04-23 19:18:53.133712+04:30 | 2018-04-23 19:56:47.702305+04:30 |         1 |      2409 | IN
 2692 | FI     | 2018-04-23 19:21:00.348889+04:30 | 2018-04-23 20:24:25.882451+04:30 |         1 |      2731 | IN
 2693 | FI     | 2018-04-23 19:30:05.908247+04:30 | 2018-04-23 20:12:36.627888+04:30 |         1 |      2591 | IN
 2694 | FI     | 2018-04-23 19:36:02.700379+04:30 | 2018-04-23 20:13:35.146002+04:30 |         1 |      2732 | IN
 2695 | FI     | 2018-04-23 19:50:15.13214+04:30  | 2018-04-23 20:09:37.168147+04:30 |         1 |      2491 | IN
 2696 | FI     | 2018-04-23 19:51:54.754169+04:30 | 2018-04-23 20:09:59.029376+04:30 |         1 |      2733 | IN
 2697 | FI     | 2018-04-23 19:53:13.529475+04:30 | 2018-04-23 20:09:49.229139+04:30 |         1 |      2734 | IN
 2698 | FI     | 2018-04-23 19:59:27.70488+04:30  | 2018-04-23 20:21:47.862433+04:30 |         1 |      1762 | IN
 2699 | FI     | 2018-04-23 19:59:57.86605+04:30  | 2018-04-23 20:22:05.171377+04:30 |         1 |      1761 | IN
 2700 | FI     | 2018-04-23 20:24:21.212784+04:30 | 2018-04-23 20:47:31.854373+04:30 |         1 |      2735 | IN
 2701 | FI     | 2018-04-23 21:58:57.308547+04:30 | 2018-04-23 22:43:20.075321+04:30 |         1 |      1705 | IN
 2702 | FI     | 2018-04-23 21:59:44.974384+04:30 | 2018-04-23 22:43:45.946989+04:30 |         1 |      1704 | IN
 2703 | FI     | 2018-04-23 22:10:20.991216+04:30 | 2018-04-23 22:40:51.16409+04:30  |         1 |      2711 | IN

And I which to get results for this day as below: 而我今天要得到的结果如下:

hour   | number
10     | 1
11     | 4
12     | 6
13     | 4
14     | 1
15     | 4
16     | 6
17     | 4
18     | 2
19     | 11
20     | 10
21     | 2
22     | 3

If I understand correct 如果我理解正确

you need to use generate_series function to create 24 hour then left join it. 您需要使用generate_series函数创建24小时,然后left join

You can add some condition on Where clause. 您可以在Where子句中添加一些条件。

Get by Hour 小时获取

SELECT    gs.hours, 
          Sum( 
          CASE 
                    WHEN start_time IS NOT NULL THEN 1 
                    WHEN end_time IS NOT NULL THEN 1 
                    ELSE 0 
          END ) AS "count" 
FROM      ( 
                 SELECT hours 
                 FROM   Generate_series(1,24) AS gs(hours) ) gs 
LEFT JOIN 
          ( 
                 SELECT *, 
                        Generate_series(start_time::timestamp, end_time::timestamp, '1 hours') invhour
                 FROM   t )t 
ON        gs.hours = To_char(t.invhour,'HH24')::integer 
GROUP BY  gs.hours

Get by Hour and Date 小时日期获取

SELECT    To_char(t.invhour,'yyyy-MM-dd') AS "dates", 
          gs.hours, 
          Sum( 
          CASE 
                    WHEN start_time IS NOT NULL THEN 1 
                    WHEN end_time IS NOT NULL THEN 1 
                    ELSE 0 
          END ) AS "count" 
FROM      ( 
                 SELECT hours 
                 FROM   Generate_series(1,24) AS gs(hours) ) gs 
LEFT JOIN 
          ( 
                 SELECT *, 
                        Generate_series(start_time::timestamp, end_time::timestamp, '1 hours') invhour
                 FROM   t )t 
ON        gs.hours = To_char(t.invhour,'HH24')::integer 
GROUP BY  gs.hours, 
          to_char(t.invhour,'yyyy-MM-dd')

sqlfiddle: http://sqlfiddle.com/#!17/717fa/1 sqlfiddle: http ://sqlfiddle.com/#!17/717fa/1

generate_series generate_series

You will need to create a set for the 24 hours, then do a left join and a group by hour. 您将需要为24小时创建一个集合,然后按小时进行左连接和分组。

Someone here has suggested using generate_series in a subquery, but personally I think recursive ctes are a little nicer for creating ranges. 这里有人建议在子查询中使用generate_series,但就我个人而言,我认为递归ctes对于创建范围会更好一些。 This way you can keep the series set outside the main query, making it a little easier to understand and maintain. 这样,您可以将系列设置保留在主查询之外,从而使其更易于理解和维护。

; WITH RECURSIVE Hours AS 
(
   SELECT 1 AS hour
   UNION ALL 
   SELECT hour + 1 FROM Hours WHERE Hour < 24
)

SELECT hour, COUNT(person_id)
FROM Hours
LEFT JOIN T on hour BETWEEN extract(hour from start_time) AND extract(hour from end_time)
GROUP BY hour
ORDER BY hour

http://sqlfiddle.com/#!17/717fa/12/0 http://sqlfiddle.com/#!17/717fa/12/0

Using the generate_series from D-Shih's answer I get the following solutions. 使用D-Shih的答案的generate_series,我得到以下解决方案。

This first solution shows all hours when at least one person was working. 第一个解决方案显示了至少一个人在工作的所有时间。

select Hour, count(1) as "Users"
from generate_series(1,24) as gs (Hour)
join Log as l
  on  date_part('hour',l.TimeFrom) <= gs.Hour
  and date_part('hour',l.TimeTo) >= gs.Hour
group by Hour
order by Hour;

If you also need the hours when nobody was working, use a left join . 如果您还需要没有人工作的时间,请使用left join This requires a change to the count() to only count when a record is found. 这要求对count()进行更改,以便仅在找到记录时才进行计数。

select Hour, count(case when l.UserId is not null then 1 end) as "Users"
from generate_series(1,24) as gs (Hour)
left join Log as l
  on  date_part('hour',l.TimeFrom) <= gs.Hour
  and date_part('hour',l.TimeTo) >= gs.Hour
group by Hour
order by Hour;

See this SQL Fiddle for some example data and output. 有关一些示例数据和输出,请参见此SQL Fiddle

Look at this article which walks through creating a Temp table in PostgreSQL (I am a SQL server person). 请看这篇文章 ,其中逐步介绍了在PostgreSQL中创建一个Temp表的过程(我是一名SQL Server人员)。

Have one column which is the hours of the day and I think your method of joining it to your table would work. 只有一栏是一天中的小时,我认为您可以将其连接到表中。

It also depends on your data in that table too. 这也取决于您在该表中的数据。 Can you provide a top 1 row data to get an idea of how it looks like? 您可以提供前1行的数据来了解它的外观吗? You might not even need the temp table. 您甚至可能不需要临时表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM