时间:2017-11-17 18:28:37

标签: postgresql crosstab generate-series

从“时间条目”表中,我正在尝试为每个用户创建每周总计的报告。

表格样本:

+-----+---------+-------------------------+--------------+
| id  | user_id | start_time              | hours_worked |
+-----+---------+-------------------------+--------------+
| 997 | 6       | 2018-01-01 03:05:00 UTC | 1.0          |
| 996 | 6       | 2017-12-01 05:05:00 UTC | 1.0          |
| 998 | 6       | 2017-12-01 05:05:00 UTC | 1.5          |
| 999 | 20      | 2017-11-15 19:00:00 UTC | 1.0          |
| 995 | 6       | 2017-11-11 20:47:42 UTC | 0.04         |
+-----+---------+-------------------------+--------------+

现在我可以运行以下内容并基本上得到我需要的内容

SELECT COALESCE(SUM(time_entries.hours_worked),0) AS total, 
  time_entries.user_id, 
  week::date

--Using generate_series here to account for weeks with no time entries when
--doing the join

FROM generate_series( (DATE_TRUNC('week', '2017-11-01 00:00:00'::date)),
                      (DATE_TRUNC('week', '2017-12-31 23:59:59.999999'::date)),
                      interval '7 day') as week LEFT JOIN time_entries
ON DATE_TRUNC('week', time_entries.start_time) = week

GROUP BY week, time_entries.user_id
ORDER BY week

这将返回

+-------+---------+------------+
| total | user_id | week       |
+-------+---------+------------+
| 14.08 | 5       | 2017-10-30 |
| 21.92 | 6       | 2017-10-30 |
| 10.92 | 7       | 2017-10-30 |
| 14.26 | 8       | 2017-10-30 |
| 14.78 | 10      | 2017-10-30 |
| 14.08 | 13      | 2017-10-30 |
| 15.83 | 15      | 2017-10-30 |
| 8.75  | 5       | 2017-11-06 |
| 10.53 | 6       | 2017-11-06 |
| 13.73 | 7       | 2017-11-06 |
| 14.26 | 8       | 2017-11-06 |
| 19.45 | 10      | 2017-11-06 |
| 15.95 | 13      | 2017-11-06 |
| 14.16 | 15      | 2017-11-06 |
| 1.00  | 20      | 2017-11-13 |
| 0     |         | 2017-11-20 |
| 2.50  | 6       | 2017-11-27 |
| 0     |         | 2017-12-04 |
| 0     |         | 2017-12-11 |
| 0     |         | 2017-12-18 |
| 0     |         | 2017-12-25 |
+-------+---------+------------+

然而,这很难解析,特别是当一周没有数据时。我想要的是一个数据透视表或交叉表,其中周是列,行是用户。并且要包含每个空值(例如,如果用户在该周或周内没有任何条目而没有来自任何用户的条目)。

像这样的东西

+---------+---------------+--------------+--------------+
| user_id | 2017-10-30    | 2017-11-06   | 2017-11-13   |
+---------+---------------+--------------+--------------+
| 6       | 4.0           | 1.0          | 0            |
| 7       | 4.0           | 1.0          | 0            |
| 8       | 4.0           | 0            | 0            |
| 9       | 0             | 1.0          | 0            |
| 10      | 4.0           | 0.04         | 0            |
+---------+---------------+--------------+--------------+

我一直在网上搜索,似乎“动态”生成交叉表的列列表是difficult。我宁愿不对它们进行硬编码,这对于日期而言似乎很奇怪。或者使用类似case with week number的内容。

我应该寻找除交叉表之外的其他解决方案吗?如果我能为每个用户提供包括所有空值在内的一系列周,我认为这样就足够了。现在我的加入策略似乎没有回归那个。

1 个答案:

答案 0 :(得分:0)

我个人会使用Date Dimension表并使用该表作为查询的基础。我发现将表格数据用于这些类型的计算要容易得多,因为它会使SQL更容易阅读和维护。有一篇关于在https://medium.com/@duffn/creating-a-date-dimension-table-in-postgresql-af3f8e2941ac的PostgreSQL中创建日期维度表的好文章,尽管你可以使用这个表的更简单版本。

最终你要做的是使用Date表作为SELECT cols FROM table部分的基础,然后加入,或者可能使用Common Table Expressions来创建计算。

如果您想要演示如何创建此类查询,我会写一个解决方案。