【发布时间】:2016-04-11 04:28:45
【问题描述】:
我正在处理许多不同产品的历史价格数据。
我有一张表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)。这些表分别大约有 15000 行(产品)和 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
【问题讨论】:
-
如果您存储每个价格的开始和结束日期(例如
tsrange),此模型可能更容易(并且更有效)。您可以使用排除约束来确保不重叠的价格区间。这可能会使插入更复杂/更慢,但如果读取性能更重要,您应该考虑到这一点。查询就像select * from history where product_id = '...' and valid_during @> timestamp '2015-03-14 12:00:00+02'一样简单 -
这不是我的结构,我必须照原样处理它......:/有什么想法吗?
-
@a_horse_with_no_name - 如果我这样做,我可能会计算
end列(可能是 MQT 或视图)或触发,然后照常插入。以后更难搞砸了。 -
@Clockwork-Muse:是的,如果添加了新价格,您可以更新插入触发器中的结束列。但是通过排除约束,您至少可以确保您永远不会得到重叠的间隔。
-
@a_horse_with_no_name - 是的,这就是我可能会 MQT 的原因 - 保证没有重叠,并且价格“不经常”变化。无论如何,greatest-n-per-group 问题的简单变体 - 现有的answers 应该可以正常工作。
标签: sql database postgresql