为 Redshift 中两个不同列的日期之间的每一天创建一个新行 SQL

Create a new row for each day between dates from two different columns in Redshift SQL

我在 Redshift 中使用 table。它包含带有一些信息和两个日期(开始日期和结束日期)的行。我似乎无法想出一种方法来为开始日期和结束日期之间的每一天创建一个新行。例如,这里的代码使一个简单的 table:

CREATE TEMPORARY TABLE dates (name VARCHAR(50), start_date DATETIME, end_date DATETIME)

INSERT INTO dates
VALUES
       ('Peter F.','2018-03-01','2018-03-05'),
       ('Sam R.','2018-04-17', '2018-04-20');

SELECT * FROM dates;

我如何从

name          | start_date                | end_date                   | 
Peter F.      | 2018-04-17 00:00:00.000000| 2018-04-20 00:00:00.000000 |
Sam R.        | 2018-03-01 00:00:00.000000| 2018-03-05 00:00:00.000000 |

至:

name          | start_date                | end_date                   | 
Peter F.      | 2018-04-17 00:00:00.000000| 2018-04-20 00:00:00.000000 |
Peter F.      | 2018-04-18 00:00:00.000000| 2018-04-20 00:00:00.000000 |
Peter F.      | 2018-04-19 00:00:00.000000| 2018-04-20 00:00:00.000000 |
Peter F.      | 2018-04-20 00:00:00.000000| 2018-04-20 00:00:00.000000 |
Sam R.        | 2018-03-01 00:00:00.000000| 2018-03-05 00:00:00.000000 |
Sam R.        | 2018-03-02 00:00:00.000000| 2018-03-05 00:00:00.000000 |
Sam R.        | 2018-03-03 00:00:00.000000| 2018-03-05 00:00:00.000000 |
Sam R.        | 2018-03-04 00:00:00.000000| 2018-03-05 00:00:00.000000 |
Sam R.        | 2018-03-05 00:00:00.000000| 2018-03-05 00:00:00.000000 |

我尝试使用这样的递归 CTE:

WITH cte
     AS (SELECT name,
                start_date,
                end_date
         FROM   dates
         UNION ALL
         SELECT name,
                Dateadd(day, 1, start_date),
                end_date
         FROM   cte
         WHERE  start_date < end_date)
SELECT *
FROM   cte

但是它给我一个错误:

[Amazon](500310) Invalid operation: relation "cte" does not exist;

希望得到一些专家的建议,因为我真的很想在 SQL 中做到这一点...提前谢谢您!

Redshift 不支持递归通用 table 表达式。

一种方法是构建 table 个数字:

create table nums(n int);
insert into nums values(0), (1), (2), (3), ...

然后您可以将数字 table 与原始 table 相结合以生成预期结果:

select
    d.name,
    dateadd(day, n.n, d.start_date) start_date,
    d.end_date
from dates d
inner join nums n 
    on dateadd(day, n.n, d.start_date) <= d.end_date

您还可以在查询中直接将数字列为派生 table,或使用 row_number() 来表示大 table。

您在这里有 2 个问题 - 第一个是如何创建日期范围 table 以加入您的开始日期和结束日期。 Redshift 的诀窍是不依赖于创建序列。通过一点点交叉连接,您可以很容易地制作一个序列。

create table ten (X) as select values (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
create table (X) thousand as 
select C.X * 100 + B.X * 10 + A.X from ten A
cross join ten B
cross join ten C;

您可以将其扩展到您喜欢的任何数量级并将其转换为天数。 (凭记忆,如果有错别字,我会道歉)

GMB 提供了一个很好的示例,说明如何为开始日期和结束日期之间的所有日期创建所有行的副本。这可能是在您的帐户 table 中处理少量行的好方法,但由于您专门谈论 Redshift,所以这个 table 很有可能很大。按开始日期和结束日期之间的天数复制一个大的 table 将导致非常大的数据产品,这将很慢地创建并且需要大量溢出到磁盘。这是问题 #2(再次假设您有很多帐户)。

如果是这种情况,那么我可以建议一种不同的方法。将您的帐户 table 与自身合并,并将数字 table 转换为日期将为您提供非常准确的日期以及每个帐户的开始和结束时间。通过一些窗口,您可以找到您可能正在寻找的大多数摘要,并且不需要制作大量数据图像来完成它。需要明确的是,table 越大,这样做的回报就越大,但对于较小的 table,加入方法将是最好的。 (2N 方法与 N 次日期)