【问题标题】:Query previous rows on a condition查询条件的前几行
【发布时间】:2020-11-17 13:51:19
【问题描述】:

我有一张关于网站上用户航班预订模式的数据表。假设以下数据是我拥有的关于我的用户的所有历史数据。

session_date 是用户进入网站并搜索特定路线的日期,而flight_date 是航班的出发日期。我已经通过session_date 订购了这张桌子。结果记录在booked

+---------+--------------+----------------+--------------+-------------+--------+
| user_id | session_date | departure_code | arrival_code | flight_date | booked |
+---------+--------------+----------------+--------------+-------------+--------+
| user1   | 7 Jan        | CA             | MY           | 8 Mar       |      1 |
| user1   | 8 Jan        | US             | MY           | 18 May      |      0 |
| user1   | 8 Jan        | US             | MY           | 18 May      |      1 |
| user1   | 8 Jan        | CA             | MY           | 19 Mar      |      0 |
| user1   | 9 Jan        | US             | MY           | 18 May      |      1 |
+---------+--------------+----------------+--------------+-------------+--------+

我想在我的表中输出一个名为previous_flight_date 的新列。新列将在每次搜索时说明先前为该特定路线预订的flight_date。即使用户多次搜索同一条路线但从未预订过,此列中的值也会为空。


+-------+--------------+----------------+--------------+-------------+--------+----------------------+
|  _id  | session_date | departure_code | arrival_code | flight_date | booked | previous_flight_date |
+-------+--------------+----------------+--------------+-------------+--------+----------------------+
| user1 | 7 Jan        | CA             | SG           | 8 Mar       |      1 | null                 |
| user1 | 8 Jan        | US             | MY           | 18 May      |      0 | null                 |
| user1 | 8 Jan        | US             | MY           | 18 May      |      1 | null                 |
| user1 | 8 Jan        | CA             | SG           | 19 Mar      |      0 | 8 Mar                |
| user1 | 2 Feb        | US             | MY           | 2 Jul       |      1 | 18 May               |
+-------+--------------+----------------+--------------+-------------+--------+----------------------+

因此,例如,在反映“3 月 8 日”的第 4 行之前,该列将为空,因为用户已经预订了从 CA-->SG 起飞的当天起飞的航班。

我尝试过使用 LAST_VALUE,但没有成功。当我有多种不同类型的路线时,我也不知道如何使用 LAG(),并且我想在某个条件下查找先前的行。如果提出解决方案会很棒!谢谢。

【问题讨论】:

    标签: sql database google-bigquery window-functions gaps-and-islands


    【解决方案1】:

    我认为您可以使用 first_value() 做到这一点。诀窍是在窗口函数中放置一个条件,打开ignore nulls 选项,然后使用窗口框架规范回顾具有相同出发/到达的前行,不包括当前行:

    select
        t.*,
        first_value(case when booked = 1 then flight_date end ignore nulls) over(
            partition by departure_code, arrival code
            order by flight_date desc
            rows between unbounded preceding and 1 preceding
        ) previous_flight_date
    from mytable t
    

    实际上窗口max() 也可以工作(然后,不需要ignore nulls):

    select
        t.*,
        max(case when booked = 1 then flight_date end) over(
            partition by departure_code, arrival code
            order by flight_date desc
            rows between unbounded preceding and 1 preceding
        ) previous_flight_date
    from mytable t
    

    【讨论】:

    • 这是一个非常好的答案,这超出了我的能力范围,但无论如何 +1 给你。
    • 我更喜欢max() 版本。
    【解决方案2】:

    我开始接受您使用LAG 的建议,但后来发现对查询进行表述相当困难。对于不使用分析函数的一种方法,我们可以尝试仅使用相关子查询来确定同一路线上最近预订的航班日期。

    SELECT user_id, session_date, departure_code, arrival_code, flight_date, booked,
           (SELECT t2.flight_date FROM yourTable t2
            WHERE t2.departure_code = t1.departure_code AND
                  t2.arrival_code = t1.arrival_code AND
                  t2.booked = 1 AND
                  t2.flight_date < t1.flight_date
            ORDER BY t2.flight_date DESC LIMIT 1) AS previous_flight_date
    FROM yourTable t1
    ORDER BY flight_date;
    

    Demo

    展示了 MariaDB 的演示,但实际上应该在 BigQuery 上运行相同的查询,没有任何问题。

    【讨论】:

    • 在用户已预订的情况下,我该如何选择航班日期?抱歉,如果我没有立即理解您的解决方案
    • @awks987 我放弃了LAG。检查我的编辑以了解另一种方式(诚然不是最好的)。
    • 好的,谢谢!不幸的是,BigQuery 似乎不支持相关查询,因此我可能必须找到一种方法将其重写为 JOIN。这可能吗?
    • 太糟糕了,因为您不能使用常规连接真正重写它。我能想到的唯一其他选择是LAG,但我不知道该怎么做。我会留下这个答案,也许可以帮助其他人在这里为您提供所需的解决方案。
    • 我已经尝试过@GMB 的答案,效果很好。感谢大家的帮助!
    【解决方案3】:

    以下是使用窗口函数的基于 SQL Server 的解决方案。 Big Query 解决方案应该类似于标准的窗口函数

    SELECT
        *
        , Previous_Flight_Date = MAX(CASE booked = 1 THEN flight_date ELSE NULL END ) 
                                 OVER (
                                        PARTITION BY user_id, departure_code, arrival_code
                                        ORDER BY flight_date
                                        ROWS UNBOUNDED PRECEDING AND 1 PRECEDING
                                 )
    FROM historicTable t
    

    【讨论】:

    • 感谢您的贡献!我最后就这样使用了window fn。真棒
    【解决方案4】:

    以下是 BigQuery 标准 SQL

    #standardSQL
    SELECT user_id, session_date, departure_code, arrival_code, flight_date, booked,
      MAX(IF(booked = 1, flight_date, NULL)) OVER(previous_flights) AS previous_flight_date
    FROM `project.dataset.table` 
    WINDOW previous_flights AS (
      PARTITION BY user_id, departure_code, arrival_code 
      ORDER BY flight_date 
      ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
    )
    

    如果应用到您的问题中的样本数据,如下例所示

    #standardSQL
    WITH `project.dataset.table` AS (
      SELECT 'user1' AS user_id, DATE '2020-01-07' AS session_date, 'CA' AS departure_code, 'SG' AS arrival_code, DATE '2020-03-08' AS flight_date, 1 AS booked UNION ALL
      SELECT 'user1', '2020-01-08', 'US', 'MY', '2020-05-18', 0 UNION ALL
      SELECT 'user1', '2020-01-08', 'US', 'MY', '2020-05-18', 1 UNION ALL
      SELECT 'user1', '2020-01-08', 'CA', 'SG', '2020-03-19', 0 UNION ALL
      SELECT 'user1', '2020-02-09', 'US', 'MY', '2020-07-02', 1
    )
    SELECT user_id, session_date, departure_code, arrival_code, flight_date, booked,
      MAX(IF(booked = 1, flight_date, NULL)) OVER(previous_flights) AS previous_flight_date
    FROM `project.dataset.table` 
    WINDOW previous_flights AS (
      PARTITION BY user_id, departure_code, arrival_code 
      ORDER BY flight_date 
      ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
    )
    -- ORDER BY flight_date
    

    输出是

    Row user_id session_date    departure_code  arrival_code    flight_date booked  previous_flight_date     
    1   user1   2020-01-07      CA              SG              2020-03-08  1       null     
    2   user1   2020-01-08      CA              SG              2020-03-19  0       2020-03-08   
    3   user1   2020-01-08      US              MY              2020-05-18  0       null     
    4   user1   2020-01-08      US              MY              2020-05-18  1       null     
    5   user1   2020-02-09      US              MY              2020-07-02  1       2020-05-18   
    

    【讨论】:

    • 感谢您的回答。我最后就这样使用了window fn。真棒
    猜你喜欢
    • 2012-02-09
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    相关资源
    最近更新 更多