我正在 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 中做到这一点......提前谢谢你!
Redshift 不支持递归公用表表达式。
一种方法是建立一个数字表:
create table nums(n int);
insert into nums values(0), (1), (2), (3), ...
然后您可以将数字表与原始表连接起来以生成预期结果:
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()
。
这里有两个问题 - 第一个问题是如何创建日期范围表以连接开始日期和结束日期。 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 方法与 N 次日期)
这对我有用:
WITH RECURSIVE cte("range_date", "start_date", "end_date") AS (
SELECT
sysdate::date AS "range_date"
, sysdate::date AS "start_date"
, DATEADD(DAY, 10, sysdate::date)::date AS "end_date"
UNION ALL
SELECT
DATEADD(DAY, 1, range_date)::date AS range_date
, start_date
, end_date
FROM cte
WHERE cte.range_date < cte.end_date
)
SELECT cte.range_date FROM cte