带日期或不带时区的带有日期或时间戳的查询中的generate_series()处理

阿克西尔

我有一个查询,用于根据由date分组的日期序列生成报告employee_id该日期应基于特定的时区,在这种情况下为'Asia / Kuala_Lumpur'。但这可能会根据用户所在时区所在的位置而改变。


SELECT 
     d::date AT TIME ZONE 'Asia/Kuala_Lumpur' AS created_date,  
     e.id,  
     e.name,
     e.division_id,
     ARRAY_AGG(
       a.id
     ) as rows,        
     MIN(a.created_at) FILTER (WHERE a.activity_type = 1) as min_time_in,
     MAX(a.created_at) FILTER (WHERE a.activity_type = 2) as max_time_out,
     ARRAY_AGG(
       CASE
           WHEN a.activity_type = 1
           THEN a.created_at
           ELSE NULL
       END
     ) as check_ins,
     ARRAY_AGG(
       CASE
           WHEN a.activity_type = 2
           THEN a.created_at
           ELSE NULL
       END
     ) as check_outs        
FROM    (SELECT MIN(created_at), MAX(created_at) FROM attendance) AS r(startdate,enddate)
  , generate_series(
        startdate::timestamp, 
        enddate::timestamp, 
        interval '1 day') g(d)
    CROSS JOIN  employee e
    LEFT JOIN   attendance a ON a.created_at::date = d::date AND e.id = a.employee_id
    where d::date = date '2020-11-20' and division_id = 1
GROUP BY 
    created_date
  , e.id
  , e.name
  , e.division_id
ORDER BY 
    created_date
  , e.id;

表的定义和样本数据attendance

CREATE TABLE attendance (
    id int,
    employee_id int,
    activity_type int,
    created_at timestamp with time zone NOT NULL
);

INSERT INTO attendance VALUES
( 1, 1, 1,'2020-11-18 07:10:25 +00:00'),
( 2, 2, 1,'2020-11-18 07:30:25 +00:00'),
( 3, 3, 1,'2020-11-18 07:50:25 +00:00'),
( 4, 2, 2,'2020-11-18 19:10:25 +00:00'),
( 5, 3, 2,'2020-11-18 19:22:38 +00:00'),
( 6, 1, 2,'2020-11-18 20:01:05 +00:00'),
( 7, 1, 1,'2020-11-19 07:11:23 +00:00'),
( 8, 1, 2,'2020-11-19 16:21:53 +00:00'), <-- Asia/Kuala_Lumpur +8 should be in 20.11 (refer to the check_outs field in the results output)
( 9, 1, 1,'2020-11-19 19:11:23 +00:00'), <-- Asia/Kuala_Lumpur +8 should be in 20.11 (refer to the check_ins field in the results output)
(10, 1, 2,'2020-11-19 20:21:53 +00:00'), <-- Asia/Kuala_Lumpur +8 should be in 20.11 (refer to the check_outs field in the results output)
(11, 1, 1,'2020-11-20 07:41:38 +00:00'),
(12, 1, 2,'2020-11-20 08:52:01 +00:00');

这是一个测试小提琴

尽管该查询应该在时区Asia / Kuala_Lumpur +8的输出中不包含第8-10行。结果显示“行”字段11,12

如何解决查询,以便它根据给定时区的日期生成报告?(意味着我可以更改Asia/Kuala_LumpurAmerica/New_York等)

有人告诉我要做这样的事情:

where created_at >= timestamp '2020-11-20' AT TIME ZONE 'Asia/Kuala_Lumpur'
and   created_at <  timestamp '2020-11-20' AT TIME ZONE 'Asia/Kuala_Lumpur' + interval '1 day'

但是我不确定如何应用它。这个小提琴中似乎无法正常工作它应包括第8、9、10、11、12行,但仅显示第8、9、10行。

欧文·布兰德斯特

数据库设计

考虑对设置进行一些修改:

CREATE TABLE employee (
  id           int PRIMARY KEY  -- !
, name         text             -- do NOT use char(n) !
, division_id  int
);

CREATE  TABLE attendance (
  id             int PRIMARY KEY  --!
, employee_id    int NOT NULL REFERENCES employee -- FK!
, activity_type  int
, created_at     timestamptz NOT NULL
);

定义PK使得汇总行变得更加容易,因为PK覆盖了GROUP BY子句中的整个行看到:

我不会使用“名称”作为列名称。这不是描述性的。每隔一列可以被命名为“名称”。考虑:

询问

SELECT *
FROM  (        -- complete employee/date grid for division in range
   SELECT g.d::date AS the_date, id AS employee_id, name, division_id
   FROM  (
      SELECT generate_series(MIN(created_at) AT TIME ZONE 'Asia/Kuala_Lumpur'
                           , MAX(created_at) AT TIME ZONE 'Asia/Kuala_Lumpur'
                           , interval '1 day')
      FROM   attendance
      ) g(d)
   CROSS  JOIN employee e
   WHERE  e.division_id = 1
   ) de
LEFT   JOIN (  -- checkins & checkouts per employee/date for division in range
   SELECT employee_id, ts::date AS the_date
        , array_agg(id) as rows
        , min(ts)             FILTER (WHERE activity_type = 1) AS min_check_in
        , max(ts)             FILTER (WHERE activity_type = 2) AS max_check_out
        , array_agg(ts::time) FILTER (WHERE activity_type = 1) AS check_ins
        , array_agg(ts::time) FILTER (WHERE activity_type = 2) AS check_outs
   FROM  (
      SELECT a.id, a.employee_id, a.activity_type, a.created_at AT TIME ZONE 'Asia/Kuala_Lumpur' AS ts  -- convert to timestamp
      FROM   employee   e
      JOIN   attendance a ON a.employee_id = e.id
   -- WHERE  a.created_at >= timestamp '2020-11-20' AT TIME ZONE 'Asia/Kuala_Lumpur' -- "sargable" expressions
   -- AND    a.created_at <  timestamp '2020-11-21' AT TIME ZONE 'Asia/Kuala_Lumpur' -- exclusive upper bound (includes all of 2020-11-20);
      AND    e.division_id = 1
      ORDER  BY a.employee_id, a.created_at, a.activity_type  -- optional to guarantee sorted arrays
   ) sub
   GROUP  BY 1, 2
   ) a USING (the_date, employee_id)
ORDER  BY 1, 2;

db <>在这里拨弄

请注意,我的查询输出了Asia / Kuala_Lumpur的本地日期和时间:

test=> SELECT timestamptz '2020-11-20 08:52:01 +0' AT TIME ZONE 'Asia/Kuala_Lumpur' AS local_ts;
      local_ts       
---------------------
 2020-11-20 16:52:01

从哪儿开始?需要了解时区的概念以及Postgres数据类型timestamp with time zonetimestamptz)与timestamp without time zonetimestamp)。否则,这将是无休止的混乱。从这里开始:

最值得注意的是,timestamptz没有存储时区:

当简单地转换timestamptzdate或时timestamp,将假定会话的当前时区设置。不是你想要的。为该AT TIME ZONE构造显式提供一个时区,以免发生这种情况。在您的小提琴中,您同时拥有:

  ...
  , generate_series(
        startdate::timestamp AT TIME ZONE 'Asia/Kuala_Lumpur', 
        enddate::timestamp AT TIME ZONE 'Asia/Kuala_Lumpur', 
        interval '1 day') g(d)
   ...

没有做你想做的。转换为(错误!)后timestamp,该AT TIME ZONE构造将值转换回timestamptz

此外,您的查询会生成所有用户的完整笛卡尔乘积以及表中的最大天数范围attendance,只是使用以下方法将其缩减为单天:

    where created_at >= timestamp '2020-11-20' AT TIME ZONE 'Asia/Kuala_Lumpur'
    and   created_at <  timestamp '2020-11-20' AT TIME ZONE 'Asia/Kuala_Lumpur' + interval '1 day'

WHERE子句最终完成了应做的事情。但是先生成完整的日期,只丢弃其中的大部分时间是没有意义的。(似乎您是在此同时我的其他提琴复制过来的?)

我注释掉了该WHERE子句,并generate_series()在查询中保留了您的优化版本作为概念证明。进一步阅读:

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

无法在没有时区的时间戳字段上加入不带时区的generate_series时间戳

来自分类Dev

根据带有时间戳和时区的文本中的日期对Excel表进行排序

来自分类Dev

oracle:查询带有可变日期的时间戳

来自分类Dev

存储/检索带或不带时区的时间戳

来自分类Dev

转换不带时区的日期时间

来自分类Dev

在 Powershell 中创建带有日期和时间戳的文件

来自分类Dev

带时区的日期时间格式

来自分类Dev

在带有 C 扩展名的 Postgres DB 中存储带时区的时间戳?

来自分类Dev

在来自 C# 的查询中插入带时区的时间戳

来自分类Dev

最小和最大日期的generate_series

来自分类Dev

如何在ms sql中查询带日期的日期=没有时间的日期

来自分类Dev

在R中创建不带时区的日期和时间序列

来自分类Dev

使用 CASE 和 generate_series() 查询,对结果时间戳进行降序排序

来自分类Dev

如何在ms sql中查询带时间的日期=没有时间的日期

来自分类Dev

从没有时区的时间戳获取日期

来自分类Dev

表中开始日期和结束日期在Postgres中的generate_series

来自分类Dev

如何将带有时区的奇怪日期时间字符串转换为时间戳(PySpark)

来自分类Dev

如何将“带时区的时间戳”映射到日期时间

来自分类Dev

Java中带时区的日期

来自分类Dev

带有时间戳和日期字段的JPA选择查询无法检索结果

来自分类Dev

带有UNIX时间戳的BusyBox日期命令设置时间

来自分类Dev

带有UNIX时间戳的BusyBox日期命令设置时间

来自分类Dev

在React Redux中订购带时间戳的日期?

来自分类Dev

如何从带时区的字符串日期获取时间戳,PHP?

来自分类Dev

带时区的HIVE日期时间格式

来自分类Dev

PostgreSQL 9.6 中带时区的时间戳

来自分类Dev

在Rails 3.2中存储带有时区的时间戳

来自分类Dev

如何在PHP中获取带有时区的日期/时间

来自分类Dev

FastAPI:请求中带有时区的日期时间不起作用

Related 相关文章

  1. 1

    无法在没有时区的时间戳字段上加入不带时区的generate_series时间戳

  2. 2

    根据带有时间戳和时区的文本中的日期对Excel表进行排序

  3. 3

    oracle:查询带有可变日期的时间戳

  4. 4

    存储/检索带或不带时区的时间戳

  5. 5

    转换不带时区的日期时间

  6. 6

    在 Powershell 中创建带有日期和时间戳的文件

  7. 7

    带时区的日期时间格式

  8. 8

    在带有 C 扩展名的 Postgres DB 中存储带时区的时间戳?

  9. 9

    在来自 C# 的查询中插入带时区的时间戳

  10. 10

    最小和最大日期的generate_series

  11. 11

    如何在ms sql中查询带日期的日期=没有时间的日期

  12. 12

    在R中创建不带时区的日期和时间序列

  13. 13

    使用 CASE 和 generate_series() 查询,对结果时间戳进行降序排序

  14. 14

    如何在ms sql中查询带时间的日期=没有时间的日期

  15. 15

    从没有时区的时间戳获取日期

  16. 16

    表中开始日期和结束日期在Postgres中的generate_series

  17. 17

    如何将带有时区的奇怪日期时间字符串转换为时间戳(PySpark)

  18. 18

    如何将“带时区的时间戳”映射到日期时间

  19. 19

    Java中带时区的日期

  20. 20

    带有时间戳和日期字段的JPA选择查询无法检索结果

  21. 21

    带有UNIX时间戳的BusyBox日期命令设置时间

  22. 22

    带有UNIX时间戳的BusyBox日期命令设置时间

  23. 23

    在React Redux中订购带时间戳的日期?

  24. 24

    如何从带时区的字符串日期获取时间戳,PHP?

  25. 25

    带时区的HIVE日期时间格式

  26. 26

    PostgreSQL 9.6 中带时区的时间戳

  27. 27

    在Rails 3.2中存储带有时区的时间戳

  28. 28

    如何在PHP中获取带有时区的日期/时间

  29. 29

    FastAPI:请求中带有时区的日期时间不起作用

热门标签

归档