【问题标题】:Create a new row for each day between dates from two different columns in Redshift SQL为 Redshift SQL 中两个不同列的日期之间的每一天创建一个新行
【发布时间】:2020-08-01 22:22:05
【问题描述】:

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

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 中做到这一点......提前谢谢你!

【问题讨论】:

    标签: sql date amazon-redshift recursive-query


    【解决方案1】:

    这里有 2 个问题 - 第一个是如何创建日期范围表以加入您的开始和结束日期。 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 提供了一个很好的示例,说明如何为开始日期和结束日期之间的所有日期创建所有行的副本。对于您的帐户表中的少量行,这可能是一种好方法,但由于您专门谈论的是 Redshift,因此该表很可能很大。按开始日期和结束日期之间的天数复制大表将导致数据产品非常大,创建速度会很慢,并且会将大量溢出到磁盘。这是问题 #2(再次假设您有很多帐户)。

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

    【讨论】:

    • 感谢您提供的信息!我有一张我描述的格式的表格,我只需要根据日期进行扩展,我无法让它以不同的方式工作。如果您能提供另一个代码示例来解决扩展表的问题 - 我将不胜感激。
    • 所以想法是不创建扩展表。问题是您想通过创建扩展表来达到什么目的?在大多数情况下,您可以生成您正在寻找的结果,而无需乘以行的副本。一个常见的情况是按天生成一个活跃帐户数量的表格。这是你正在做的那种东西吗?如果是这样,我可以按照这些思路发布答案
    • 我还有其他采用这种扩展格式的表格,我需要使用一些键和日期来连接它们。问题是,这些表可能不一定在 LEFT 表中的 start_dateend_date 之间的每一天都有时间戳,在这种情况下,我仍然需要日期,但这些表中的 LEFT JOIN 会出现空值表。那有意义吗?我看不出有什么不同的方法
    • 换句话说,将我提供的第一个表作为示例提供为 LEFT 表。然后,在右侧,我有一个类似于我在示例中使用的第二个表格,但在开始日期和结束日期之间的start_date 列中的每个日期都没有一行。但是,当我加入这两个表时,我希望在 start_date 中为每个日期保留一行,即使它在第二个表中不存在(如果是 LEFT 连接,只会导致 NULL 值) .
    【解决方案2】:

    Redshift 不支持递归公用表表达式。

    一种方法是建立一个数字表:

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

    然后您可以将 numbers 表与原始表连接以生成预期结果:

    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
    

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

    【讨论】:

    • 非常感谢@GBM!这完美地工作。我想我会需要生成一个包含一长列数字的表格,以确保它始终有效?有时我的约会相隔一两年。关于如何使其更易于维护的任何建议?
    • @Georgiy:是的,您需要尽可能多的数字,因为您的日期之间可能有几天......您可以使用您选择的工具以编程方式为数字表生成inserts 列表.或者,您可以在一张大桌子上使用row_number(),例如:insert into numbers select row_number() over() from mylargetable
    • 谢谢@GBM,感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多