在 Athena 上转换为带时区的时间戳失败

Posted

技术标签:

【中文标题】在 Athena 上转换为带时区的时间戳失败【英文标题】:converting to timestamp with time zone failed on Athena 【发布时间】:2018-11-22 18:49:51 【问题描述】:

我正在尝试创建以下视图:

CREATE OR REPLACE VIEW view_events AS 
(
   SELECT
     "rank"() OVER (PARTITION BY "tb1"."innerid" ORDER BY "tb1"."date" ASC) "r"
   , "tb2"."opcode"
   , "tb1"."innerid"
   , "tb1"."date"
   , From_iso8601_timestamp(tb1.date) as "real_date"
   , "tb2"."eventtype"
   , "tb1"."fuelused"
   , "tb1"."mileage"
   , "tb1"."latitude"
   , "tb1"."longitude"
   FROM
     rt_message_header tb1
   , rt_messages tb2
   WHERE ((("tb1"."uuid" = "tb2"."header_uuid") AND ("tb2"."opcode" = '39')) AND ("tb2"."type" = 'event'))
   ORDER BY "tb1"."innerid" ASC, "tb1"."date" ASC
)

它给了我以下错误:

您的查询有以下错误: 不支持的 Hive 类型:带时区的时间戳

但是,当我自己运行查询时,它工作正常,并且 From_iso8601_timestamp 被提到 here 作为有效的日期函数。

谁能告诉我我做错了什么?

【问题讨论】:

【参考方案1】:

不幸的是,Athena 并不完全支持所有 Presto 功能,它有 limitations,并且在技术上落后于 Presto 几个版本。有一些尝试使 Athena 与 AWS Glue Metastore 紧密集成,虽然基于 Hive 的 Metastore 有一些不一致之处。我希望 Spark、Hive、Glue、Athena、Presto 等能够与同一个元存储一起工作,这会让生活更轻松,但回到你的问题:

document about an older teradata fork of Presto 提到了一些关于 presto 时间戳的问题:

Presto 声明带/不带时区的时间戳的方法不是 sql标准。在 Presto 中,两者都使用单词 TIMESTAMP 声明, 例如TIMESTAMP '2003-12-10 10:32:02.1212' 或 TIMESTAMP '2003-12-10 10:32:02.1212 UTC'。时间戳确定有无 时区取决于您是否在末尾包含时区 时间戳。在其他系统中,时间戳被明确声明为 TIMESTAMP WITH TIME ZONE 或 TIMESTAMP WITHOUT TIME ZONE

从 Athena 派生出来的 Presto 版本确实支持 timestamptimestamp with timezone,但正如 teradata 文档中提到的那样,这应该不是问题。真正的问题是Athena does not support timestamp with timezone。

您链接到的 presto 文档显示该函数返回的值属于不受支持的类型 timestamp with timezone,因此您需要将其转换为受支持的其他内容。 Athena 允许函数和强制转换为不受支持的数据类型,这是一个疏忽,希望这将得到纠正,但现在您必须解决它。

您需要做的是在该函数调用周围使用CAST() 函数,这会将类型从timestamp with time zone 更改为timestamp

不幸的是,您可能无法将字符串直接转换为时间戳,尽管这取决于字符串的格式。您也不能使用在字符串之前写入timestamp 的转换样式,例如不能timestamp '2018-01-01 15:00:00',原因我将在下面解释。

from_iso1601_timestamp() 函数返回的类型

SELECT typeof("real_date") AS real_date_type
FROM
(
SELECT From_iso8601_timestamp('2018-01-01T15:00:00Z') as "real_date"
)

带有时区的时间戳

这不起作用

SELECT typeof("real_date") AS real_date_type
FROM
(
SELECT CAST('2018-01-01T15:00:00Z' AS timestamp) as "real_date"
)

SQL 错误 [FAILED]:INVALID_CAST_ARGUMENT:值无法转换为 时间戳

这种类型的 Casting 还返回带时区的时间戳:(

请注意,其中的 SELECT 部分有效,它表示它是 timestamp,但由于某些内部不一致的原因,您无法创建视图并且会出现错误。

CREATE OR replace VIEW test 
AS 
SELECT typeof( "real_date" ) AS real_date_type
FROM
(
SELECT  timestamp '2018-01-01 15:00:00' as "real_date"
)

SQL 错误 [FAILED]:无法初始化类 com.facebook.presto.util.DateTimeZoneIndex

无论出于何种原因,创建视图都需要 java 类,而解析 select 中的值则不需要。这是一个应该解决的错误。

这很好用

CREATE OR REPLACE VIEW test
AS
SELECT typeof("real_date") AS real_date_type
FROM
(
SELECT CAST(From_iso8601_timestamp('2018-01-01T15:00:00Z') AS timestamp) as "real_date"
)

【讨论】:

【参考方案2】:

您可以在 Athena 中通过 Timestamp 数据类型 (dt) 使用以下语法:

SELECT id,dt,dt AT TIME ZONE 'America/New_York' as dateTimeNY FROM Table

【讨论】:

【参考方案3】:

在我最近正在做的事情上遇到了类似的事情。 AWS Support 向我指出了 Davos 解决方案,但它最终对我的情况不起作用。我最终得到的解决方案是:

create or replace view db_name.vw_name AS
select
    from_unixtime(cast(to_unixtime(current_timestamp) AS bigint)) as field_name
from db_name.tbl_name

这会将current_timestamp(即timestamp with time zone)的输出转换为timestamp

如果要验证字段的数据类型,可以使用:

select typeof(field_name) from db_name.vw_name

希望有帮助!

【讨论】:

感谢您的补充,这似乎是一个有用的替代方案。

以上是关于在 Athena 上转换为带时区的时间戳失败的主要内容,如果未能解决你的问题,请参考以下文章

DateTime<Utc> 编译但不是 DateTime<Local> 查询表,其列定义为带时区的时间戳

如何将表示 EPOCH 时间的整数转换为 Athena (Presto) 中的时间戳?

Amazon Athena - 将时间戳转换为日期?

Debezium 时间戳问题,无法转换为本地时区

ORA-01870 试图将日期转换为带有时区的时间戳

PostgreSQL 错误地从没有时区的时间戳转换为有时区的时间戳