在期间内加上最后一个期间前选择

时间:2018-07-13 08:45:04

标签: sql postgresql

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

-

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

var status = document.getElementById("status");
    var bio = document.getElementById("bio");

其中存储了某些产品的历史价格:

var req = new XMLHttpRequest();
      req.open("GET", 'https://api.scratch.mit.edu/users/' + username);
      req.send();
      req.onreadystatechange = function() {
        if (req.readyState === 4 && req.status === 200) {
          country.textContent = JSON.parse(req.responseText).profile.country;
          avatar.alt = username + "'s avatar";
          avatar.src = "https://cdn2.scratch.mit.edu/get_image/user/" + JSON.parse(req.responseText).id + "_90x90.png";
          avatar.className = "";
          if(JSON.parse(req.responseText).profile.bio !== "") {
            bio.textContent = JSON.parse(req.responseText).profile.bio;
          }

          else {
            bio.innerHTML = "<em>Not specified</em>";
          }

          if(JSON.parse(req.responseText).profile.status !== "") {
            status.textContent = JSON.parse(req.responseText).profile.status;
          }

          else {
            status.innerHTML = "<em>Not specified</em>";
          }

        }};

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

简单方法:

table price_history (
    product_id,
    price,
    changed_date
)

不好,因为不包括从2018年1月1日到第一次价格变化之间每种产品的单独价格:

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

但是从期初就知道价格至关重要。

因此,除了期间内的价格变动外,还必须包括之前的最后变动。 结果应该是这样的:

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

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

编辑:

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

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

结果:

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');

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

5 个答案:

答案 0 :(得分:1)

我想这就是您要寻找的

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

答案 1 :(得分: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 :(得分:0)

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

如果要解决现有结构的问题,在PostgresSQL中,您可以使用LIMIT 1来获取change_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

答案 3 :(得分:0)

您需要第一个更改日期和所有其他日期>“ 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'

答案 4 :(得分:0)

使用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