【问题标题】:SQL 'AT TIME ZONE', query wide and with a 'SELECT' all columns (tablename.*) expressionSQL'AT TIME ZONE',查询范围和使用'SELECT'所有列(表名。*)表达式
【发布时间】:2017-01-05 19:42:48
【问题描述】:

当有必要时,我在我的数据库中使用timestamp(3) with time zone 作为表格,这在我的情况下几乎是任何情况。

我需要在比以下更复杂的查询中选择表的所有列。

我的问题是如何使用 SQL at time zone '<TIMEZONE>' 获取所需时区的时间戳(带时区),以获得像 q.* 这样的选择表达式,其中之一是时间戳(带时区)列。 我可能有相同情况的子查询。 是否有表达式可以在查询范围内实现这一点?

SELECT
    q.*, -- created_at timestamp (with time zone) is already in here
    q.created_at AT TIME ZONE 'EET', --instead of this redundant column selection
    u.name AS author,
    u.reputation,
    CASE WHEN count(t.*)=0 THEN '[]' ELSE json_agg(t.*) END as tags
FROM posts q

-- authors
JOIN users u
ON q.author_id = u.id

-- tags
left join post_has_tag p_h_t
on q.id = p_h_t.post_id
left join tags t
on p_h_t.tag_id = t.id

WHERE q.post_type = 'question'
group by q.id, u.id;

【问题讨论】:

    标签: sql postgresql timezone timestamp


    【解决方案1】:

    没有将所有数据类型timestamptz 的列转换为timestamp 的神奇设置。但您可以(临时)设置所需的目标时区以调整timestamptz 值的显示文本表示):

    BEGIN;
    SET LOCAL timezone = 'EET';
    SELECT q.*, -- including created_at timestamp as original timestamptz type
         , u.name AS author,
         , ...
    
     -- do something with your data
    
    COMMIT;  -- or ROLLBACK; doesn't matter for just SELECT
    

    与应用AT TIME ZONE 构造相同,后者实际上将timestamp 转换为timestamptz,反之亦然。详情:

    SET LOCAL 的效果持续到交易结束。

    而且(如@Matt already mentioned)通常(即使有点贵)最好使用实际时区名称而不是时区缩写。时区名称将 DST 和时区的其他特性考虑在内:

    SET LOCAL timezone = 'Europe/Istanbul';
    

    演示

    BEGIN;
    SET timezone = 'Europe/Istanbul';
    SELECT now();
    
    now
    -----------------------------
    2016-08-29 22:39:09.275647+03
    
    SET timezone = 'UTC';
    SELECT now();
    
    now
    -----------------------------
    2016-08-29 19:39:09.275647+00
    
    COMMIT;

    【讨论】:

    • 我现在有点困惑。我在所有表格中都使用timestamp with time zone。我需要根据他们的时区向不同的时区用户显示正确的时间表示。在写作时我不做任何特殊的事情,只是插入或使用CREATE_TIMESTAMP。我的数据库可以位于与 GMT 不同的时区。但是,当我阅读时,我计划阅读 at time zone 子句,使用完整的时区名称,我会这样做,以便用户看到他们时区的时间。但我看到的唯一问题是at time zone 的语法它不能应用于p.* 内的文件
    • 在阅读了提到的 SO 问题和您@Erwin 参与的其他问题后,我得出了关于时间戳使用的结论和应用程序设计,非常感谢。我错过了什么吗?
    • Postgres 如何知道“他们的时区”?如果他们使用会话中设置的相应时区进行查询,则显示会自动调整。
    • 我会以某种方式从请求中获取他们的时区。我需要从用户那里获取正确查询 Postgres 的任何方式,无论是设置会话时区,还是使用 at time zone,我可能对使用 timestamp with timezone 的第二个 SQL 构造的想法有误
    • 我在 pgAdmin3 SQL 编辑器中收到 query result with 5 rows discarded. Query returned successfully with no result in 20 msec. 消息。当我省略开始并提交时,它会从表中给出 5 行,同时应用上面设置的时区是什么原因?
    【解决方案2】:

    如果您的列是timestamp 类型,那么使用AT TIME ZONE 是将它们转换为特定时区的正确方法。

    但是,不要使用EET。使用基于特定地区的时区 from this list,例如 Europe/Bucharest - 或任何适合您的。

    或者,如果您的列是timestamp with time zone 类型,那么您可以设置会话的时区,postgres 将为您进行转换:

    SET TIME ZONE 'Europe/Bucharest'
    

    您应该阅读the docs 以了解这两种时间戳类型的区别。

    【讨论】:

    • “会话”是指当前事务还是查询,因为它也是隐式事务?
    • these docs。基本上,SESSION 是默认值,其范围适用于与数据库的整个连接。 LOCAL 用于当前事务。
    • 目前我在问题和类似问题中的查询中使用SET TIME ZONE 构造。但是,post 表中的时间戳,第一个表 - 连接中最左边的表在设置的时区中不被考虑。 tags 表中的那些是在指定时间一开始的子句输出的。这可能是什么原因?
    • timestamptimestamptz 是两种不同的类型。会话/本地时区设置仅影响timestamptz(又名“带时区的时间戳”)
    • 所有与时间和日期相关的字段都是我的timestamp(3) with time zone 值,在posts 表和tags 表中。
    猜你喜欢
    • 2017-01-05
    • 2014-07-10
    • 1970-01-01
    • 2011-03-14
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    相关资源
    最近更新 更多