2016-08-01 40 views
1

我想从谷歌大查询表中获得每日销售额的总和。我为此使用了以下代码。如何生成日期系列以在谷歌BiqQuery中占据缺席日期?

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 0 31行的销售额。示例如下所示。该月的第四天没有销售。所以其总和应为0.

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

是否可以在Big-query中执行?基本上日期列应该是从1 - 28/29/30或31日的系列,具体取决于当年的月份

回答

5

您可以使用下面生成飞在给定范围内的所有日期(以下面的例子是从2015年6月1日至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 
))) 
+0

几乎解决了我的问题。假设我想要获得前几个月的销售额,我该如何改变它? (需要每个月执行一次),并且数据应该仅在特定月份从1到28/29/30或第31个 –

+0

添加到我的答案中 –

1

为了实现这些目的,有一个“日历”表,这个表只列出了所有日期在一定范围内。对于您的具体问题,只需要一个表格,其数字为1到31.获取此表格的一个快速方法是使用这些数字制作电子表格,将其另存为csv文件并将该文件作为表格导入BigQuery 。

然后您将left outer join设置到此表上,其中ifnull(sales,0) as sales

如果你想让每月的天数(28--31)是正确的,你基本上有两种选择。要么创建一个适当的日历表,其中涵盖了几年,并且您使用年,月和日来加入。或者您使用1-31号数字的简单表格,并根据月份和年份删除数字。

+0

是不是有什么办法像在这里http://stackoverflow.com/questions/11391085/getting-date-list-in-a-range-in-postgresql。 –

+0

没有什么等同于'generate_series',所以你不能轻松地生成一个列表。一旦你有一个表中的数字列表,你可以使用'date_add'函数来生成一个日期列表,将当天的日期偏移x天。 ('date_add'在此处定义:https://cloud.google.com/bigquery/query-reference#date_add) – oulenz

+0

每个月的日期不相等,并且还取决于年份(feb中的天数)。那么如何克服呢? –

0

中的每一天对于标准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 
1

使用标准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 
; 

UPDATE

或者,还是简单的使用功能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 
;