如何使用 snowsql 在给定日期范围内构造日期数组?

Posted

技术标签:

【中文标题】如何使用 snowsql 在给定日期范围内构造日期数组?【英文标题】:How do I construct an array of dates in a given date range using snowsql? 【发布时间】:2020-03-06 18:07:41 【问题描述】:

给定两个日期,我想创建一个数组来保存这些日期之间的所有天数,然后过滤它以仅包含月末的天数。

例如,用start_date2019-01-31end_date2019-06-30,我会构造数组

[
  '2019-01-31',
  '2019-02-28',
  '2019-03-31',
  '2019-04-30',
  '2019-05-31',
  '2019-06-30'
]

【问题讨论】:

您的示例/数组中的日期很奇怪,包括不存在的 31-06-2019。我想你想要连续的日期,但你的例子看起来像“几乎月底”。请使用正确的详细信息更新您的问题。 【参考方案1】:

确切地理解您想要什么有点困难,但从请求的输出来看,该数组似乎包含两个日期限制之间的每月日期。我尝试使用 javascript 来实现以避免可怕的“不支持的子查询”错误,但很难计算日期、格式化它们并从 JavaScript 返回到 SQL。 所以我最终得到了一个 SQL UDF:

CREATE OR REPLACE FUNCTION ARRAY_MONTHS_BETWEEN("FROM" DATE, "TO" DATE)
RETURNS ARRAY AS 
'
  SELECT ARRAY_AGG(DATEADD(MONTH, "MONTH" , "FROM")) A
  FROM (SELECT ROW_NUMBER() OVER (ORDER BY NULL) - 1 "MONTH"
        FROM TABLE(GENERATOR(ROWCOUNT => 1000)))
  WHERE "MONTH" <= CEIL(DATEDIFF(MONTHS, "FROM", "TO"))
';

除了日期常量外,使用此函数可能会很棘手,因为您可能会打开潘多拉魔盒,其中包含雪花相关子查询。但话又说回来,也许不是。

【讨论】:

点头,我们有两个进程做类似的事情,一个在会话中创建一个临时表,其中使用插入常量日期的创建表(使用 mustache 语法)和另一个提前/落后的日期维度表......并使用它们来满足需求,同时避免大量的表扫描。 感谢您的帮助。我设法通过在 Snowflake 中创建日历表来实现这一目标 用于日期计算的“彩虹表”非常棒,例如。在数据仓库环境中。使用半结构化数据,例如。阵列,可能会以(轻微?)效率和处理为代价来简化存储。【参考方案2】:

Snowflake LAST_DAY 函数可用于获取两个日期之间的每个月的最后一天 https://docs.snowflake.net/manuals/sql-reference/functions/last_day.html.

SELECT 
  ARRAY_AGG(LAST_DAY(MY_DATE))
FROM (
  SELECT
    LAST_DAY(DATEADD(MONTH, SEQ4(), '2019-01-31')) AS MY_DATE
  FROM TABLE(GENERATOR(ROWCOUNT=>20000))
  WHERE MY_DATE <= '2019-06-30'
);

上述查询还将结果包装在一个数组https://docs.snowflake.net/manuals/sql-reference/functions/array_agg.html

[
  "2019-01-31",
  "2019-02-28",
  "2019-03-31",
  "2019-04-30",
  "2019-05-31",
  "2019-06-30"
]

如前所述,您可以选择使用此 sql 来创建新的用户定义函数:

CREATE FUNCTION LAST_DATES_TO_ARRAY(FROM_D DATE, TO_D DATE)
RETURNS ARRAY 
AS 
$$
SELECT ARRAY_AGG(LAST_DAY(MY_DATE))
FROM (
  SELECT
    LAST_DAY(DATEADD(MONTH, SEQ4(), FROM_D)) AS MY_DATE
  FROM TABLE(GENERATOR(ROWCOUNT=>20000))
  WHERE MY_DATE <= TO_D
)
$$;

【讨论】:

这太棒了!感谢您的帮助,如果您对 Snowflake 社区的 Select Star 计划感兴趣,如果您是 Snowflake 的注册用户,该计划会为您的个人资料添加积分,如果您有兴趣,请查看页面并注册:community.snowflake.com/s/selectstar【参考方案3】:

基于之前响应者提供的内容,以下内容应该适合您。

CREATE OR REPLACE FUNCTION LAST_DATES_TO_ARRAY(FROM_DT DATE, TO_DT DATE)
RETURNS ARRAY 
AS 
$$
SELECT ARRAY_AGG(DISTINCT LAST_DAY(MY_DATE))
FROM (
  SELECT
    LAST_DAY(DATEADD(MONTH, SEQ4(), FROM_DT)) AS MY_DATE
  FROM TABLE(GENERATOR(ROWCOUNT=>2000000))
  WHERE MY_DATE <= TO_DT
  ORDER BY 1
)
$$;

SELECT LAST_DATES_TO_ARRAY(DATEADD('YEARS', -1, CURRENT_DATE()), CURRENT_DATE()) AS my_array;

 MY_ARRAY
["2018-11-30",
 "2018-12-31",
 "2019-01-31",
 "2019-02-28",
 "2019-03-31",
 "2019-04-30",
 "2019-05-31",
 "2019-06-30",
 "2019-07-31",
 "2019-08-31",
 "2019-09-30",
 "2019-10-31"]

【讨论】:

【参考方案4】:

另一种仅限 sql 的解决方案 - 开始和结束日期进入 current_date() 点。

生成 10000 年的所有月末将今天放在中间 (365|180 * 10000) 非常快,然后只需在放入数组之前使用 sed 开始和结束日期来预测答案。运行时间为 202 毫秒

没有功能,使用缓存,测试较少等。

select array_agg(distinct my_date) 
from ( 
      SELECT 
      last_day( DATEADD(DAY, -1*SEQ4(), CURRENT_DATE()+(180*10000)),month) AS MY_DATE 
      FROM TABLE(GENERATOR(ROWCOUNT => (365*10000) ))  
      where 
          my_date between 
          current_date() -39 and 
          current_date() +80 
      )

【讨论】:

以上是关于如何使用 snowsql 在给定日期范围内构造日期数组?的主要内容,如果未能解决你的问题,请参考以下文章

使用javascript验证用户输入日期是不是在给定的日期范围内

SQL:如何显示给定范围内的所有日期?

在python中生成给定范围内的所有日期

PostgreSQL:如何在给定日期范围内的每一天为每个帐户选择最后余额? [复制]

VBA - 删除不在给定日期范围内的数据

Java - 在指定的给定月份 - 年份范围内打印每个月的第一个和最后一个日期