缺少数据的窗口函数

时间:2016-06-08 19:05:21

标签: sql google-bigquery

假设我有一个表(MyTable),如下所示:

item_id  |  date
----------------
     1   | 2016-06-08
     1   | 2016-06-07
     1   | 2016-06-05
     1   | 2016-06-04
     1   | 2016-05-31
     ...
     2   | 2016-06-08
     2   | 2016-06-06
     2   | 2016-06-04
     2   | 2016-05-31
     ...
     3   | 2016-05-31
     ...

我想建立一个每周汇总表,报告正在运行的7天窗口。该窗口基本上会说"在前7天内报告了多少独特的item_id"?

因此,在这种情况下,输出表看起来像:

date      | weekly_ids
----------------------
2016-05-31|  3       # All 3 were present on the 31st
2016-06-01|  3       # All 3 were present on the 31st which is < 7 days before the 1st
2016-06-02|  3       # Same
2016-06-03|  3       # Same
2016-06-04|  3       # Same
2016-06-05|  3       # Same
2016-06-06|  3       # Same
2016-06-07|  3       # Same
2016-06-08|  2       # item 3 was not present for the entire last week so it does not add to the count.

我试过了:

SELECT
    item_id,
    date,
    MAX(present) OVER (
        PARTITION BY item_id
        ORDER BY date
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS is_present
FROM (
    # Inner query
    SELECT
        item_id,
        date,
        1 AS present,
    FROM MyTable
)
GROUP BY date
ORDER BY date DESC

这个感觉就像它朝着正确的方向前进一样。但实际上,当日期不存在(日期太多)时,窗口会在错误的时间范围内运行,并且当item_id没有时,它也不会输出日期的记录。 t存在(即使它出现在上一个日期)。这个问题有一个简单的解决方案吗?

如果它有用且必要

  • 可以硬编码最早的日期
  • 我还可以获得现有的所有item_id的表格。
  • 此查询只能在BigQuery上运行,因此BQ特定的函数/语法是公平的游戏,并且不能在BigQuery上运行的SQL函数/语法对我不起帮助......

2 个答案:

答案 0 :(得分:2)

我创建了一个临时表来保存日期,但是,您可能会因为这些联接向数据库添加永久表而受益。相信我会减少头痛。

DECLARE @my_table TABLE
(
    item_id int,
    date DATETIME
)
INSERT @my_table SELECT 1,'2016-06-08'
INSERT @my_table SELECT 1,'2016-06-07'
INSERT @my_table SELECT 1,'2016-06-05'
INSERT @my_table SELECT 1,'2016-06-04'
INSERT @my_table SELECT 1,'2016-05-31'
INSERT @my_table SELECT 2,'2016-06-08'
INSERT @my_table SELECT 2,'2016-06-06'
INSERT @my_table SELECT 2,'2016-06-04'
INSERT @my_table SELECT 2,'2016-05-31'
INSERT @my_table SELECT 3,'2016-05-31'

DECLARE @TrailingDays INT=7
DECLARE @LowDate DATETIME='01/01/2016'
DECLARE @HighDate DATETIME='12/31/2016'

DECLARE @Calendar TABLE(CalendarDate DATETIME)
DECLARE @LoopDate DATETIME=@LowDate

WHILE(@LoopDate<=@HighDate) BEGIN
    INSERT @Calendar SELECT @LoopDate
    SET @LoopDate=DATEADD(DAY,1,@LoopDate)
END 



SELECT
    date=HighDate,      
    weekly_ids=COUNT(DISTINCT item_id)
FROM
(
    SELECT
        HighDate=C.CalendarDate,
        LowDate=LAG(C.CalendarDate, @TrailingDays,0) OVER (ORDER BY C.CalendarDate)
    FROM
        @Calendar C 
    WHERE
        CalendarDate BETWEEN @LowDate AND @HighDate
)AS X
LEFT OUTER JOIN @my_table MT ON MT.date BETWEEN LowDate  AND HighDate
GROUP BY
    LowDate,
    HighDate

答案 1 :(得分:1)

尝试以下示例。它可以为您提供探索方向 纯粹GBQ - 遗留SQL

SELECT date, items FROM (
  SELECT 
    date, COUNT(DISTINCT item_id) OVER(ORDER BY sec RANGE BETWEEN 60*60*24*2 PRECEDING AND CURRENT ROW) AS items
  FROM (
    SELECT 
      item_id, date, timestamp_to_sec(timestamp(date)) AS sec
    FROM (

      SELECT calendar.day AS date, MyTable.item_id AS item_id
      FROM (
        SELECT DATE(DATE_ADD(TIMESTAMP('2016-05-28'), pos - 1, "DAY")) AS day
        FROM (
             SELECT ROW_NUMBER() OVER() AS pos, *
             FROM (FLATTEN((
             SELECT SPLIT(RPAD('', 1 + DATEDIFF(TIMESTAMP(CURRENT_DATE()), TIMESTAMP('2016-05-28')), '.'),'') AS h
             FROM (SELECT NULL)),h
        )))
      ) AS calendar
      LEFT JOIN (
        SELECT date, item_id
        FROM
          (SELECT 1 AS item_id, '2016-06-08' AS date),
          (SELECT 1 AS item_id, '2016-06-07' AS date),
          (SELECT 1 AS item_id, '2016-06-05' AS date),
          (SELECT 1 AS item_id, '2016-06-04' AS date),
          (SELECT 1 AS item_id, '2016-05-28' AS date),
          (SELECT 2 AS item_id, '2016-06-08' AS date),
          (SELECT 2 AS item_id, '2016-06-06' AS date),
          (SELECT 2 AS item_id, '2016-06-04' AS date),
          (SELECT 2 AS item_id, '2016-05-31' AS date),
          (SELECT 3 AS item_id, '2016-05-31' AS date),
          (SELECT 3 AS item_id, '2016-06-05' AS date)
      ) AS MyTable
      ON calendar.day = MyTable.date
    )
  )
)
GROUP BY date, items
ORDER BY date  

请注意

  • 最早的日期 - 2016-05-28 - 在日历子查询中硬编码
  • 窗口大小控制在60 * 60 * 24 * 2预设和当前行的范围内;如果您需要7天 - 表达式应为60 * 60 * 24 * 6
  • 请记住BigQuery Legacy SQL中的COUNT(DISTINCT)的具体内容