【问题标题】:Using LAG() and PARTITION BY to return rows IF within 10 days of date使用 LAG() 和 PARTITION BY 在日期的 10 天内返回行 IF
【发布时间】:2019-02-09 12:37:47
【问题描述】:

我有一个名为fruits 的表格:

id  fruit_bought   quantity   date
1 | orange       | 100      | 2018-01-10
2 | apple        | 50       | 2018-02-05
3 | orange       | 75       | 2018-03-07
4 | orange       | 200      | 2018-03-15
5 | apple        | 10       | 2018-03-17
6 | orange       | 20       | 2018-03-20

我想返回包含 fruit_boughtorange 的行 如果 在过去 10 天内的任何时间购买了任何橙子,从日期 2018-03-20(与id 6 的行)。

例如:

  • 2018-03-20 开始,在这一天(行id 6)购买了橙子
  • 在此之前 10 天是否购买过任何橙子?是:在“2018-03-15”(行 id 4
  • 在此日期前 10 天是否购买过任何橙子?是:在“2018-03-07”(行 id 3
  • 在此日期前 10 天是否购买过任何橙子?没有。

最后,我尝试创建的查询将返回 id 为 3、4 和 6(但不是 1)的行。

到目前为止,我的查询如下:

SELECT *, LAG(date, 1) OVER (PARTITION BY fruit_bought) FROM fruits
WHERE fruit_bought = 'orange';

这将返回每一行,其中fruit_boughtorange并且它添加了一个额外的lag 列。

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    此答案基于Gordon Linoff's idea, 但有一些调整:

    • FILTER is not implemented for pure window functions 类似于 Postgresql 11 中的 Lead() 或 lag()(目前)。所以使用WHERE fruit_bought='orange'作为整个内部SELECT的条件。

    • 要保证选择具有最后日期的行,请使用LEAD(date, 1, '-infinity')。这使得next_date 的默认值等于-infinity 时间戳。因此date >= next_date - interval '10 day' 将在最后一个日期为 TRUE。

    • 让我们将 10 天内的行称为一个集群。要仅从最后一个集群中选择行, 计算一个累积总和,计算 cond 为 FALSE 的次数(因为 FALSE 值分隔集群):

      SUM(CASE WHEN cond IS TRUE THEN 0 ELSE 1 END) OVER (ORDER BY date DESC) AS cluster_num
      

      只选择cluster_num等于0的行。因为我们ORDER BY date DESC,所以第0个簇是最后一个簇。


    SELECT *
    FROM (
        SELECT *, SUM(CASE WHEN cond IS TRUE THEN 0 ELSE 1 END) OVER (ORDER BY date DESC) AS cluster_num
        FROM (
            SELECT *, date >= next_date - interval '10 day' AS cond
            FROM (
                SELECT id, fruit_bought, date, 
                    LEAD(date, 1, '-infinity') 
                    OVER (PARTITION BY fruit_bought ORDER BY date) AS next_date 
                FROM fruits 
                WHERE fruit_bought='orange'
                -- restrict date here to specify an "initial date"
                AND date <= '2018-04-01'  
            ) t1
        ) t2
    ) t3
    WHERE cond AND cluster_num = 0
    ORDER BY date ASC
    

    产量

    | id | fruit_bought |       date |  next_date | cond | cluster_num |
    |----+--------------+------------+------------+------+-------------|
    |  3 | orange       | 2018-03-07 | 2018-03-15 | t    |           0 |
    |  4 | orange       | 2018-03-15 | 2018-03-20 | t    |           0 |
    |  6 | orange       | 2018-03-20 |  -infinity | t    |           0 |
    

    设置:

    CREATE TABLE fruits (
        fruitid INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
        id INT,
        fruit_bought TEXT,
        quantity INT,
        date DATE);
    
    INSERT INTO fruits (id, fruit_bought, quantity, date)
    VALUES (1,'orange',100,'2018-01-10')
    , (2,'apple',50,'2018-02-05')
    , (3,'orange',75,'2018-03-07')
    , (4,'orange',200,'2018-03-15')
    , (5,'apple',10,'2018-03-17')
    , (6,'orange',20,'2018-03-20')
    , (7,'orange',20,'2018-01-09');
    

    【讨论】:

    • 有没有办法提供一个初始日期来搜索?例如:从日期2018-03-20 开始并倒退?您示例中的查询似乎搜索了我的整个表格。
    • 当然——您可以在最里面的SELECT 中使用date &lt;= initial_date 条件来限制哪些日期是整个查询的一部分。我已经编辑了帖子以说明我的意思。
    【解决方案2】:

    一种方法是 lag() 和 filter 。 . .但像这样使用:

    select f.*
    from (select f.*,
                 lag(date) filter (where fruit_bought = 'orange') over (order by date) as prev_orange_date
          from fruits f
         ) f
    where prev_orange_date >= date - interval '10 day';
    

    不过,exists 也会浮现在脑海中:

    select f.*
    from fruits f
    where exists (select 1
                  from fruits f2
                  where f2.fruit_bought = 'orange' and
                        f2.date >= f.date - interval '10 day' and
                        f2.date < f.date
                 );
    

    这两个查询都假定日期是唯一的,如您的示例所示。如果你们有联系,那么每个人都可以工作。但是,您必须指定如何处理购买橙子的日子。

    【讨论】:

      【解决方案3】:

      试试

      select fruit_bought, min(date), max(date) group by fruit_bought
      having (max(date) - min(date)) <= 10;
      

      【讨论】:

        猜你喜欢
        • 2020-12-02
        • 1970-01-01
        • 2020-06-29
        • 2022-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-15
        • 1970-01-01
        相关资源
        最近更新 更多