generate_series() 方法在 Redshift 中失败

Posted

技术标签:

【中文标题】generate_series() 方法在 Redshift 中失败【英文标题】:generate_series() method fails in Redshift 【发布时间】:2014-05-10 16:45:06 【问题描述】:

当我运行 SQL 查询时:

 select generate_series(0,g)
 from ( select date(date1) - date(date2) as g from mytable ;

它返回一个错误:

 INFO:  Function "generate_series(integer,integer)" not supported.
 ERROR:  Specified types or functions (one per INFO message) not supported 
 on Redshift tables.

但是当我运行这个查询时:

select  generate_series(0, g) from (select 5 as g)

它返回以下响应:

 generate_series
-----------------
 0
 1
 2
 3
 4
 5
(6 rows)

为什么第二个查询有效,而第一个查询失败?

【问题讨论】:

显然第一个子查询返回interval 而不是integer(因为:“generate_series(integer, interval) 不存在”) 在错误消息中查看我的编辑。 您没有使用 PostgreSQL。您正在使用 Amazon Redshift。 因为您报告这是与 Redshift 一起使用的,而 redshift 没有版本化,因此任何人都无法使用旧版本,我不明白为什么这个问题应该保持开放。我投票结束。 silota.com/docs/recipes/… 【参考方案1】:

Redshift 不完全支持generate_series() 函数。请参阅开发者指南的Unsupported PostgreSQL functions 部分:

在具体示例中,第二个查询完全在领导节点上执行,因为它不需要扫描任何实际的表数据,而第一个查询是尝试选择数据,因此将在计算节点上执行(s )。

更新:

generate_series 现在正在使用 Redshift。

SELECT CURRENT_DATE::TIMESTAMP  - (i * interval '1 day') as date_datetime 
FROM generate_series(1,31) i 
ORDER BY 1

这将生成过去 30 天的日期

【讨论】:

这并不能解决计算节点不支持 generate_series 的问题。如果您尝试将此选择连接到表或在 CTE 中使用它,或创建临时表,则会发生相同的错误。【参考方案2】:

您可以使用window function 来获得类似的结果。这需要一个现有的表(如stv_blocklist)来播种,该表至少具有您需要的行数,但不能太多,这可能会减慢速度。

with days as (
    select (dateadd(day, -row_number() over (order by true), sysdate::date)) as day 
    from [other_existing_table] limit 30
)
select day from days order by 1 asc

您可以使用此方法获取其他时间范围以及用于分桶目的。此版本会生成前一天的所有分钟数,因此您可以对其进行左连接并存储您的数据。

with buckets AS (
    select (dateadd(minute, -row_number() over (order by true), sysdate::date)) as minute 
    from [other_table] limit 1440
)
select minute from buckets order by 1 asc

我可能第一次看到这个here。

【讨论】:

【参考方案3】:

你说得对,这在 Redshift 上不起作用。 见here。

你可以使用这样的东西

with ten_numbers as (select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0)
,generted_numbers AS
(
    SELECT (1000*t1.num) + (100*t2.num) + (10*t3.num) + t4.num-5000 as gen_num
    FROM ten_numbers AS t1
      JOIN ten_numbers AS t2 ON 1 = 1
      JOIN ten_numbers AS t3 ON 1 = 1
      JOIN ten_numbers AS t4 ON 1 = 1
)
select  gen_num from generted_numbers
where gen_num between -10 and 0
order by 1;

【讨论】:

【参考方案4】:

您没有使用 PostgreSQL。您正在使用 Amazon Redshift。

与 Redshift 表一起使用时,Amazon Redshift 不支持 generate_series。它就在错误消息中说明。

要么使用真正的 PostgreSQL,要么如果你需要 Redshift 的功能,你也必须在 Redshift 的限制范围内工作。

您的第二个示例有效,因为它不使用任何 Redshift 表

【讨论】:

这不是leader节点唯一支持的功能吗? @Naveen 也许是现在,但不是我写这篇文章的时候。【参考方案5】:

上面解释了为什么它不起作用。不过,“我们能做些什么呢?”这个问题仍然存在。开了。

如果您在任何平台上开发 BI 系统(无论是否支持生成器),拥有带有数字和日期序列的维度表都会非常方便。如何在 Redshift 中创建一个?

    在 Postgres 中,使用生成器生成必要的序列 导出为 CSV 在 Redshift 中创建具有相同架构的表 将步骤 2 中的 CSV 导入 Redshift

假设您创建了一个名为calendar 的非常简单的表:

 id, date
 1, 2017-01-01
 2, 2017-01-02
 ..., ...
 xxx, 2020-01-01

所以您的查询将如下所示:

SELECT t.id, t.date_1, t.date_2, c.id as date_id, c.date
FROM mytable t
JOIN calendar c
ON c.date BETWEEN t.date_1::date AND t.date_2::date
ORDER BY 1,4

在日历表中,您还可以设置周、月、季度、工作日(周一、周二等)的第一个日期,这使得此类表对于基于时间的聚合非常有效。

【讨论】:

【参考方案6】:

您将需要使用领导节点支持的功能。诀窍是使用您想要的 any 表中的row_number() 函数。 假设我们想从 10 天前到现在生成一个日期序列:

   SELECT DATEADD('day', -n, (CURRENT_DATE+1)) AS generated_date
   FROM (SELECT ROW_NUMBER() OVER () AS n FROM my_table LIMIT 10) n
   ORDER BY generated_date DESC

我们得到:

generated_date
2020-06-24 00:00:00
2020-06-23 00:00:00
2020-06-22 00:00:00
2020-06-21 00:00:00
2020-06-20 00:00:00
2020-06-19 00:00:00
2020-06-18 00:00:00
2020-06-17 00:00:00
2020-06-16 00:00:00
2020-06-15 00:00:00

【讨论】:

【参考方案7】:

我在实践中发现,从现有表中计算行数对于我的使用来说太慢了(并且引入了依赖项)。

将“计数行”答案与上面 Jon Scott 的答案相结合,我们得到了我们在公司使用的这个解决方案:

with n as (
    select 1 union select 2 union select 3 union select 4 union select 5 union
    select 6 union select 7 union select 8 union select 9 union select 10
  )
  select row_number() over () as n
  from n n1, n n2, n n3, n n4, n n5, n n6

【讨论】:

【参考方案8】:

这在这里有效(pg-9.3.3)也许您的问题只是 Redshift-“功能”的结果?

CREATE TABLE mytable
        ( date1 timestamp
        , date2 timestamp
        );
INSERT INTO mytable(date1,date2) VALUES
( '2014-03-30 12:00:00' , '2014-04-01 12:00:00' );

SELECT  generate_series(0, ss.g) FROM
   ( SELECT date(date2) - date(date1) AS g
     FROM mytable
   ) ss ;

【讨论】:

OP 询问红移。

以上是关于generate_series() 方法在 Redshift 中失败的主要内容,如果未能解决你的问题,请参考以下文章

Amazon Redshift 中的 generate_series 函数

如何使用 generate_series() 生成值网格

在没有时区的时间戳的字段上加入没有时区的 generate_series 时间戳时遇到问题

如何在 postgreSQL 中创建 generate_series 函数?

不能在 Redshift 上使用 JOIN 和 generate_series

在 createNativeQuery 中使用 generate_series 时出错