数据库存储历史价格变化,如何计算特定时间点的价格?

时间:2016-01-06 22:25:00

标签: sql database postgresql

我正在处理许多不同产品的历史价格数据。

我有一张表products,列出了所有带有名称,描述等的产品,用uuid标识它们。然后还有另一个表history,用于存储每个产品的每次价格变化。价格可能(通常会)每天变化很多次。

现在我想计算每个产品在特定时间点的价格,比如2015年3月14日中午12点。我怎样才能在SQL中执行此操作?

我能够为一种产品做到这一点:

SELECT product_id, price, date 
FROM history 
WHERE product_id = 'aa6d9976-e9ae-4478-486e-097e86c1e5fe' 
AND (date-'2015-03-14 12:00:00+02') < interval '1 second' 
ORDER BY diff DESC LIMIT 1

  ->   aa6d9976-e9ae-4478-486e-097e86c1e5fe    109     2015-03-14 11:55:00+01

但我希望一组查询中的所有产品。我的想法是获取所有产品并将该表与历史记录联系起来,为每个产品选择合适的价格,但我对后者失败了:

SELECT products.product_id, name, price, date 
FROM products 
  LEFT JOIN history ON products.product_id = history.product_id 
WHERE date "is the greatest value that is still somewhat smaller than" '2015-03-14 12:00:00+01'

你如何正确地写下我试图用引号表达的内容?

我使用PostgreSQL(虽然我之前主要使用过MySQL)。这些表分别约为1.5万(产品)和5000万(历史)行。

如果您喜欢某些示例数据:

PRODUCTS

product_id                              name

aa6d9976-e9ae-4478-486e-097e86c1e5fe    One
8da97d50-540e-4fdb-d032-7f443a9869a0    Two
b51654ea-6190-4ed2-5e23-7075ffd3b472    Three


HISTORY

id  product_id                              price   date

1   aa6d9976-e9ae-4478-486e-097e86c1e5fe    100     2015-03-14 09:30:00+01
2   aa6d9976-e9ae-4478-486e-097e86c1e5fe    110     2015-03-14 10:48:00+01
3   b51654ea-6190-4ed2-5e23-7075ffd3b472    9       2015-03-14 11:01:00+01
4   8da97d50-540e-4fdb-d032-7f443a9869a0    49      2015-03-14 11:27:00+01
5   aa6d9976-e9ae-4478-486e-097e86c1e5fe    109     2015-03-14 11:55:00+01
6   b51654ea-6190-4ed2-5e23-7075ffd3b472    8       2015-03-14 13:59:00+01
7   aa6d9976-e9ae-4478-486e-097e86c1e5fe    110     2015-03-14 16:10:00+01
8   8da97d50-540e-4fdb-d032-7f443a9869a0    48      2015-03-14 19:34:00+01
9   8da97d50-540e-4fdb-d032-7f443a9869a0    49      2015-03-14 23:30:00+01
10  aa6d9976-e9ae-4478-486e-097e86c1e5fe    103     2015-03-14 23:33:00+01


DESIRED OUTPUT

id                                      name    price   date

aa6d9976-e9ae-4478-486e-097e86c1e5fe    One     109     2015-03-14 11:55:00+01
8da97d50-540e-4fdb-d032-7f443a9869a0    Two     49      2015-03-14 11:27:00+01
b51654ea-6190-4ed2-5e23-7075ffd3b472    Three   9       2015-03-14 11:01:00+01

2 个答案:

答案 0 :(得分:0)

首先编写查询以查找小于查询每个产品的日期的最大日期。这看起来像这样:

    select product_id, MAX(date) date 
    from history
    where date < '3/14/2015 12:00:00'
    group by product_id

然后,您可以使用productshistory表加入该子查询,以获得所需的结果:

select products.*, history.price, history.date
from products
left join
    (
    select product_id, MAX(date) date 
    from history
    where date < '3/14/2015 12:00:00'
    group by product_id
    ) PriceDates
on products.product_id = PriceDates.product_id
join history
on PriceDates.product_id = history.product_id
    and PriceDates.date = history.date

答案 1 :(得分:0)

使用窗口函数lead()查找给定product_id next 对应记录(顺便说一句:我将date重命名为zdate。{{ 1}}是列的错误名称,因为它是数据类型的名称)

date

{product_id,zdate}上的索引可能会有所帮助;-)

结果:

SELECT h0.* 
FROM history h0
JOIN (
    SELECT  id
    , zdate AS start_date
    , lead(zdate, 1, 'infinity' ) OVER (PARTITION BY product_id
                                        ORDER BY zdate) AS end_date
    FROM history
    ) h1 ON h0.id = h1.id
    AND h1.start_date <= '2015-03-14 12:00:00+01'
    AND h1.end_date > '2015-03-14 12:00:00+01'
    ;