【问题标题】:How to generate date series to occupy absent dates in google BiqQuery?如何生成日期系列以占用谷歌 BigQuery 中的缺席日期?
【发布时间】:2016-12-06 06:00:12
【问题描述】:

我正在尝试从谷歌大查询表中获取每日销售额。我为此使用了以下代码。

select Day(InvoiceDate) date, Sum(InvoiceAmount) sales from test_gmail_com.sales 
where year(InvoiceDate) = Year(current_date()) and
Month(InvoiceDate) = Month(current_date())
group by date order by date

从上面的查询中,它只给出了表中每日销售额的总和。有可能有些日子没有任何销售。对于这种情况,我需要获取日期并且总和应该为 0。例如,每个月应该有 30 0r 31 行销售总和。示例如下所示。该月的第 4 天没有销售。所以它的总和应该是0。

date | sales
-----+------
1    |   259
-----+------
2    |   359
-----+------
3    |   45
-----+------
4    |    0
-----+------
5    |  156

是否可以在大查询中进行?基本上日期列应该是从 1 到 28/29/30 或 31 的系列,具体取决于一年中的月份

【问题讨论】:

    标签: sql google-bigquery


    【解决方案1】:

    生成日期列表,然后在顶部加入您需要的任何表格似乎是最简单的。我用了generate_date_array + unnest,看起来很干净。

    要生成天数列表(每行一天):

      SELECT
      *
      FROM 
        UNNEST(GENERATE_DATE_ARRAY('2018-10-01', '2020-09-30', INTERVAL 1 DAY)) AS example
    

    【讨论】:

      【解决方案2】:

      使用标准 SQL 方言和generate_array 函数简化代码:

      WITH serialnum AS (
        SELECT
          sn
        FROM
          UNNEST(GENERATE_ARRAY(0, 
                                DATE_DIFF(DATE_ADD(DATE_TRUNC(CURRENT_DATE()
                                                            , MONTH)
                                                , INTERVAL 1 MONTH)
                                        , DATE_TRUNC(CURRENT_DATE(), MONTH)
                                        , DAY) - 1)
                                ) AS sn
      ), date_seq AS (
      SELECT
          DATE_ADD(DATE_TRUNC(CURRENT_DATE(), MONTH),
                  INTERVAL(sn) DAY) AS this_day
      FROM
        serialnum
      )
      SELECT
          Day(InvoiceDate) date
          , Sum(IFNULL(InvoiceAmount, 0)) sales
      FROM
          date_seq
          LEFT JOIN
          test_gmail_com.sales
      ON
          date_seq.this_day = DAY(test_gmail_com.sales.InvoiceDate)
      WHERE
          year(InvoiceDate) = Year(current_date())
          and
          Month(InvoiceDate) = Month(current_date())
      GROUP BY
          date
      ORDER BY
          date
      ;
      

      更新

      或者,仍然使用generate_date_array 函数更简单:

      WITH date_seq AS (
      SELECT
        GENERATE_DATE_ARRAY(DATE_TRUNC(CURRENT_DATE(), MONTH), 
                            DATE_ADD(DATE_ADD(DATE_TRUNC(CURRENT_DATE(), MONTH)
                                              , INTERVAL 1 MONTH)
                                     , INTERVAL -1 DAY)
                            , INTERVAL 1 DAY)
          AS this_day
      )
      SELECT
          Day(InvoiceDate) date
          , Sum(IFNULL(InvoiceAmount, 0)) sales
      FROM
          date_seq
          LEFT JOIN
          test_gmail_com.sales
      ON
          date_seq.this_day = DAY(test_gmail_com.sales.InvoiceDate)
      WHERE
          year(InvoiceDate) = Year(current_date())
          and
          Month(InvoiceDate) = Month(current_date())
      GROUP BY
          date
      ORDER BY
          date
      ;
      

      【讨论】:

      【解决方案3】:

      对于标准 SQL

      WITH
      
      splitted AS (
        SELECT
          *
        FROM
          UNNEST( SPLIT(RPAD('',
                1 + DATE_DIFF(CURRENT_DATE(), DATE("2015-06-01"), DAY),
                '.'),''))),
        with_row_numbers AS (
        SELECT
          ROW_NUMBER() OVER() AS pos,
          *
        FROM
          splitted),
        calendar_day AS (
        SELECT
          DATE_ADD(DATE("2015-06-01"), INTERVAL (pos - 1) DAY) AS day
        FROM
          with_row_numbers)
      SELECT
        *
      FROM
        calendar_day
      ORDER BY
        day DESC
      

      【讨论】:

        【解决方案4】:

        您可以使用下方动态生成给定范围内的所有日期(在下面的示例中,它是从 2015-06-01 到 CURRENT_DATE() 的所有日期 - 通过更改您可以控制要生成的日期范围的日期)

        SELECT DATE(DATE_ADD(TIMESTAMP("2015-06-01"), pos - 1, "DAY")) AS calendar_day
        FROM (
             SELECT ROW_NUMBER() OVER() AS pos, *
             FROM (FLATTEN((
             SELECT SPLIT(RPAD('', 1 + DATEDIFF(TIMESTAMP(CURRENT_DATE()), TIMESTAMP("2015-06-01")), '.'),'') AS h
             FROM (SELECT NULL)),h
        )))
        

        所以,现在 - 您可以将它与 LEFT JOIN 一起用于您的表格来计算所有日期。请参阅下面的潜在示例

        SELECT
          calendar_day,
          IFNULL(sales, 0) AS sales
        FROM (
          SELECT DATE(DATE_ADD(TIMESTAMP("2015-06-01"), pos - 1, "DAY")) AS calendar_day
          FROM (
               SELECT ROW_NUMBER() OVER() AS pos, *
               FROM (FLATTEN((
               SELECT SPLIT(RPAD('', 1 + DATEDIFF(TIMESTAMP(CURRENT_DATE()), TIMESTAMP("2015-06-01")), '.'),'') AS h
               FROM (SELECT NULL)),h
          )))
        ) AS all_dates
        LEFT JOIN (
          SELECT DAY(InvoiceDate) DATE, SUM(InvoiceAmount) sales 
          FROM test_gmail_com.sales 
          WHERE YEAR(InvoiceDate) = YEAR(CURRENT_DATE()) AND
          MONTH(InvoiceDate) = MONTH(CURRENT_DATE())
          GROUP BY DATE 
        )
        ON DATE = calendar_day  
        

        我想获得前几个月的销售额

        下面给出了上个月的所有天数

        SELECT DATE(DATE_ADD(DATE_ADD(DATE_ADD(CURRENT_DATE(), -1, "MONTH"), 1 - DAY(CURRENT_DATE()), "DAY"), pos - 1, "DAY")) AS calendar_day
        FROM (
             SELECT ROW_NUMBER() OVER() AS pos, *
             FROM (FLATTEN((
             SELECT SPLIT(RPAD('', 1 + DATEDIFF(DATE_ADD(CURRENT_DATE(), - DAY(CURRENT_DATE()), "DAY"), DATE_ADD(DATE_ADD(CURRENT_DATE(), -1, "MONTH"), 1 - DAY(CURRENT_DATE()), "DAY")), '.'),'') AS h
             FROM (SELECT NULL)),h
        )))
        

        【讨论】:

        • 上述解决方案适用于 bigquery legacy。根据stackoverflow.com/questions/38306016/… ,对于最新版本,以下查询有效 SELECT day FROM UNNEST( GENERATE_DATE_ARRAY(DATE('2015-06-01'), CURRENT_DATE(), INTERVAL 1 DAY) ) AS day
        【解决方案5】:

        出于这些目的,有一个“日历”表是实用的,该表仅列出特定范围内的所有日期。对于您的具体问题,有一个数字为 1 到 31 的表格就足够了。获取此表格的一种快速方法是使用这些数字制作一个电子表格,将其保存为 csv 文件,然后将此文件作为表格导入 BigQuery .

        然后你将left outer join你的结果集放到这个表上,ifnull(sales,0) as sales

        如果您希望每月的天数 (28--31) 正确,您基本上有两种选择。您可以创建一个涵盖几年的适当日历表,并使用年、月和日加入。或者您使用带有数字 1--31 的简单表格,并根据月份和年份删除数字。

        【讨论】:

        • 这里没有stackoverflow.com/questions/11391085/…这样的方法。
        • 没有与generate_series 等效的东西,因此您不能轻松地即时生成列表。在表格中获得数字列表后,您可以使用函数 date_add 通过将今天的日期偏移 x 天来生成日期列表。 (date_add 在这里定义:cloud.google.com/bigquery/query-reference#date_add
        • 每个月的日期不相等,它也取决于年份(2 月的天数)。那么如何克服呢?
        猜你喜欢
        • 1970-01-01
        • 2021-12-19
        • 2011-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多