【问题标题】:select within period plus last before period选择期间内加上期间前的最后一个
【发布时间】:2018-07-13 08:45:04
【问题描述】:

感谢所有花时间评论和回答的人。

-

我有一个这样的价格历史表(伪代码):

table price_history (
    product_id,
    price,
    changed_date
)

存储部分产品的历史价格:

1,  1.0, '2017-12-18'
1,  1.2, '2017-12-20'
1,  0.9, '2018-04-20'
1,  1.1, '2018-07-20'
1,  1.3, '2018-07-22'
2, 10.0, '2017-12-15'
2, 11.0, '2017-12-16'
2,  9.9, '2018-01-02'
2, 10.3, '2018-04-04

现在我想要某个时期内某些产品的价格。例如。从 2018 年 1 月 1 日到现在。

简单的方法:

    SELECT * FROM price_history
      WHERE product_id in (1,2) AND changed_date >= 2018-01-01

不可以,因为每个产品从 2018-01-01 到第一次价格变化的单独价格不包括在内:

1,  0.9, '2018-04-20'
1,  1.1, '2018-07-20'
1,  1.3, '2018-07-22'
2,  9.9, '2018-01-02'
2, 10.3, '2018-04-04

但了解期初价格至关重要。

所以,除了期间内的价格变化外,之前的最后一次变化也必须包括在内。 结果应该是这样的:

1,  1.2, '2017-12-20'
1,  0.9, '2018-04-20'
1,  1.1, '2018-07-20'
1,  1.3, '2018-07-22'
2, 11.0, '2017-12-16'
2,  9.9, '2018-01-02'
2, 10.3, '2018-04-04

问:如何指定这样的select语句?

编辑:

Ajay Gupta 的测试场景和解决方案

CREATE TABLE price_history (
        product_id integer,
        price float,
        changed_date timestamp
    );

INSERT INTO price_history (product_id,price,changed_date) VALUES
    (1, 1.0, '2017-12-18'),
    (1, 1.2, '2017-12-20'),
    (1, 0.9, '2018-04-20'),
    (1, 1.1, '2018-07-20'),
    (1, 1.3, '2018-07-22'),
    (2, 10.0, '2017-12-15'),
    (2, 11.0, '2017-12-16'),
    (2, 9.9, '2018-01-02'),
    (2, 10.3, '2018-04-04');

获胜选择:

with cte1 as
  (Select *, lag(changed_date,1,'01-01-1900')
  over(partition by product_id order by changed_date)
    as FromDate from price_history),
  cte2 as (Select product_id, max(FromDate)
           as changed_date from cte1
           where '2018-01-01'
             between FromDate and changed_date group by product_id)
  Select p.* from price_history p
    join cte2 c on p.product_id = c.product_id
  where p.changed_date >= c.changed_date
  order by product_id,changed_date;

结果:

 product_id | price |    changed_date     
------------+-------+---------------------
          1 |   1.2 | 2017-12-20 00:00:00
          1 |   0.9 | 2018-04-20 00:00:00
          1 |   1.1 | 2018-07-20 00:00:00
          1 |   1.3 | 2018-07-22 00:00:00
          2 |    11 | 2017-12-16 00:00:00
          2 |   9.9 | 2018-01-02 00:00:00
          2 |  10.3 | 2018-04-04 00:00:00

我必须承认,这超出了我有限的 (PG-)SQL 技能。

【问题讨论】:

  • 您提到 changed_date >= 2018-01-01 但预计 2017 年的数据会出现在预期输出中??你能把你的预期输出和条件再清楚一点吗
  • 请标记您的关系型数据库
  • @Zaynul Abadin Tuhin - 我认为文字足够清晰,很遗憾听到它不是。我想要的是:某段时间内产品的价格。从那个时期开始。显然,由于选择错误,直到 2018 年 4 月 20 日产品 1 的第一次更改和 p2 的第一次更改,我才得到 2018 年 1 月 1 日之间的价格。我需要的是一个更复杂的替代品,它可以提供所需的结果。

标签: sql postgresql


【解决方案1】:

使用Lagcte

with cte1 as (
  Select *, 
         lag(changed_date,1,'01-01-1900') over(partition by product_id order by changed_date) as FromDate 
  from price_history
), cte2 as  (
   Select product_id, max(FromDate) as changed_date 
   from cte1  
   where '2018-01-01' between FromDate and changed_date 
   group by product_id
)
Select p.* 
from price_history p 
  join cte2 c on p.product_id = c.product_id
where p.changed_date >= c.changed_date;

【讨论】:

    【解决方案2】:

    我猜这就是你要找的东西

    SELECT Top 1 * FROM price_history WHERE product_id in (1,2) AND changed_date < 2018-01-01
    UNION ALL
    SELECT * FROM price_history WHERE product_id in (1,2) AND changed_date >= 2018-01-01
    

    【讨论】:

    • Top 1是Sql Server语法,本题是关于postgreSQL的
    【解决方案3】:

    您需要第一个更改日期和所有其他日期>“2018-01-01”

     select product_id,price, changed_date
        from
        (
        select product_id,price, changed_date,
        row_number() over(partition by product_id order by changed_date ) as rn
        from price_history
        ) x
        where x.rn = 2 and product_id in (1,2);
    union all
    select product_id,price, changed_datefrom from price_history
    where product_id in (1,2) and changed_date >= '2018-01-01'
    

    【讨论】:

    • @Gisela 有帮助吗?
    【解决方案4】:

    如果您确实可以选择更改表结构,另一种方法是在表中同时包含 start_date 和 end_date,这样您的记录将不依赖于上一行/下一行,并且您的查询变得更容易编写。见Slowly changing dimension - Type 2

    如果你想解决现有结构的问题,在PostgresSQL中你可以使用LIMIT 1获取changed_date之前的最新记录:

    SELECT 
        * 
    FROM 
        price_history 
    WHERE 
        product_id in (1,2) 
        AND changed_date >= '2018-01-01'
    UNION ALL
    -- this would give you the latest price before changed_date
    SELECT 
        * 
    FROM 
        price_history 
    WHERE 
       product_id in (1,2) 
       AND changed_date < '2018-01-01'
    ORDER BY
       changed_date DESC
    LIMIT 1
    

    【讨论】:

      【解决方案5】:

      union 的解决方案仍然更简单,但在其他答案中没有正确实现。所以:

      SELECT * FROM price_history
            WHERE product_id in (1,2) AND changed_date >= '2018-01-01'
      union all
      (
        select distinct on (product_id)
          *
        from price_history
        where product_id in (1,2) AND changed_date < '2018-01-01'
        order by product_id, changed_date desc)
      order by product_id, changed_date;
      

      Demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-13
        • 1970-01-01
        • 1970-01-01
        • 2019-05-13
        相关资源
        最近更新 更多