复杂的SQL查询,任何地方都没有答案:(

时间:2018-07-02 13:08:38

标签: sql google-bigquery

假设我在SQL数据库事件中具有属性dateuser_id。我在表event中有10条记录:

1.  user_id=1, date=2018.04.10
2.  user_id=1, date=2018.04.11
3.  user_id=1, date=2018.04.13
4.  user_id=1, date=2018.04.17
5.  user_id=1, date=2018.04.18
6.  user_id=2, date=2018.04.12
7.  user_id=2, date=2018.04.12
8.  user_id=2, date=2018.04.13
9.  user_id=2, date=2018.04.15
10. user_id=2, date=2018.04.16

是否可以使用标准SQL语法编写查询,该语法仅向我显示每个用户的记录,该日期至少相差2天。 所以:

1.  user_id=1, date=2018.04.10   will be in result
2.  user_id=1, date=2018.04.11   not in result, only 1 day difference
3.  user_id=1, date=2018.04.12   will be in result, 2 days dif from record nbr 1.
4.  user_id=1, date=2018.04.17   will be in result, 5 days dif from record nbr 3.
5.  user_id=1, date=2018.04.18   no in result, only 1 day dif from record nbr4.
6.  user_id=2, date=2018.04.12   will be in result
7.  user_id=2, date=2018.04.12   not in result, 0 day difference
8.  user_id=2, date=2018.04.13   not in result, only 1 day dif from record nbr. 6.
9.  user_id=2, date=2018.04.15   will be in result, 2 days dif from record nbr 8.
10. user_id=2, date=2018.04.16   not in result, only 1 day dif from record nbr. 9.

拜托,伙计们,我办公室里没人能帮我:( 我将在Google BigQuery中使用此查询

3 个答案:

答案 0 :(得分:3)

只需使用lag()

select e.*
from (select e.*,
             lag(prev_date) over (partition by user_id order by date) as prev_date
      from events e
     ) e
where prev_date is null or 
      date > date_add(prev_date, interval 2 day);

注意:这可能对绑定日期有些棘手。最好在order by中添加第二列,以便顺序稳定。

如果只有这两列,则可以在最低级别上进行不同处理以解决此问题:

select e.*
from (select e.*,
             lag(prev_date) over (partition by user_id order by date) as prev_date
      from (select distinct e.* from events e) e
     ) e
where prev_date is null or 
      date > date_add(prev_date, interval 2 day);

答案 1 :(得分:1)

正如戈登·利诺夫(Gordon Linoff)的评论所言:“ SQL表代表无序集”,并且在编写查询以解决此类问题时意识到这一点是至关重要的。

解决该问题的另一种方法是退后一步,考虑在结果集中包含某些给定行的 not 原因。根据您提供的数据样本,似乎存在另一行(即与给定行不同),该行具有相同的user_id,并且日期等于或早于日期的一天从给定的行。使用WHERE NOT EXISTS轻松地将其转换为标准的相关子查询。它比您模糊的“日期至少相差2天”要精确得多,这就是“与什么有区别?”的问题。

SELECT USER_ID,DATE
  FROM EVENT E
 WHERE NOT EXISTS (SELECT * FROM EVENT E2
                    WHERE E2.USER_ID = E.USER_ID
                          AND
                          <appropriate comparison here between E2.DATE and E.DATE>
                          AND
                          <appropriate comparison here to ascertain only distinct rows are processed>);

使用窗口函数也可以正确解决您的问题,但是正如Gordon Linoff所指出的那样,必须对它们在关系上以及任何组的“第一”和“最后”行中的行为保持警惕。

编辑

您似乎对第3行说了一个问题,“结果将是nbr 1记录的第2天。”您为什么不在这里与第2行进行比较?因为尚未为结果集保留第2行,并且您希望比较始终与“保留的最后一行”进行?这使得问题/解决方案本质上是递归的,并使我的解决方案和戈登的解决方案都不适用。

答案 2 :(得分:1)

以下是用于BigQuery标准SQL

正如欧文(Erwin)在EDIT中的回答-problem/solution inherently recursive and makes both mine and Gordon's solution inapplicable中提到的那样,因此以下解决方案解决了递归问题。此外,它还可以通过将其解析为DATE类型并在完成所有计算后正确处理您的日期字段-将其格式化为您的符号。等等

  
#standardSQL
CREATE TEMPORARY FUNCTION qualified_entries(arr ARRAY<DATE>)
RETURNS ARRAY<DATE>
LANGUAGE js AS """
  var result = []; prev = null; day = 1000*60*60*24;
  for (i = 0; i < arr.length; i++) {
    if (i == 0 || Math.round((arr[i].getTime() - prev)/day) > 2) {
      result.push(arr[i]);
      prev = arr[i].getTime();
    }
  };
  return result;
""";
SELECT user_id, FORMAT_DATE('%Y.%m.%d', dt) dt FROM (
  SELECT user_id, qualified_entries(ARRAY_AGG(PARSE_DATE('%Y.%m.%d', dt)))dt
  FROM `project.dataset.table`
  GROUP BY user_id
), UNNEST(dt) dt

您可以使用下面的问题中的虚拟数据来测试/使用

#standardSQL
CREATE TEMPORARY FUNCTION qualified_entries(arr ARRAY<DATE>)
RETURNS ARRAY<DATE>
LANGUAGE js AS """
  var result = []; prev = null; day = 1000*60*60*24;
  for (i = 0; i < arr.length; i++) {
    if (i == 0 || Math.round((arr[i].getTime() - prev)/day) > 2) {
      result.push(arr[i]);
      prev = arr[i].getTime();
    }
  };
  return result;
""";
WITH `project.dataset.table` AS (
  SELECT 1 user_id, '2018.04.10' dt UNION ALL
  SELECT 1, '2018.04.11' UNION ALL
  SELECT 1, '2018.04.13' UNION ALL
  SELECT 1, '2018.04.17' UNION ALL
  SELECT 1, '2018.04.18' UNION ALL
  SELECT 2, '2018.04.12' UNION ALL
  SELECT 2, '2018.04.12' UNION ALL
  SELECT 2, '2018.04.13' UNION ALL
  SELECT 2, '2018.04.15' UNION ALL
  SELECT 2, '2018.04.16' 
)
SELECT user_id, FORMAT_DATE('%Y.%m.%d', dt) dt FROM (
  SELECT user_id, qualified_entries(ARRAY_AGG(PARSE_DATE('%Y.%m.%d', dt)))dt
  FROM `project.dataset.table`
  GROUP BY user_id
), UNNEST(dt) dt
-- ORDER BY user_id, dt

结果为

Row     user_id     dt   
1       1           2018.04.10   
2       1           2018.04.13   
3       1           2018.04.17   
4       2           2018.04.12   
5       2           2018.04.15