我有两个正确的表
用户旅程
id timestamp bus
1 00:10 12
1 16:10 12
2 14:00 23
公共汽车
id timestamp price
12 00:00 1.3
12 00:10 1.5
12 00:20 1.7
12 18:00 2.0
13 00:00 3.0
我的目标是查找每个用户今天在旅行上花费了多少。
在我们的案例中,用户在00:10乘坐12号公交车并支付1.5,在16:10乘坐另一辆公交车,价格升至1.7。该人今天总共支付了3.2。我们始终采用最新的更新价格。
我已经使用大型子查询来完成此操作,它看起来效率很低。有谁有一个很好的解决方案?
请参阅http://sqlfiddle.com/#!17/10ad6/2
或构建模式:
drop table if exists journeys;
create table journeys(
id numeric,
timestamp timestamp without time zone,
bus numeric
);
truncate table journeys;
insert into journeys
values
(1, '2018-08-22 00:10:00', 12),
(1, '2018-08-22 16:10:00', 12),
(2, '2018-08-22 14:00:00', 23);
-- Bus Prices
drop table if exists bus;
create table bus (
bus_id int,
timestamp timestamp without time zone,
price numeric
);
truncate table bus;
insert into bus
values
(12, '2018-08-22 00:10:00', 1.3),
(12, '2018-08-22 00:10:00', 1.5),
(12, '2018-08-22 00:20:00', 1.7),
(12, '2018-08-22 18:00:00', 2.0),
(13, '2018-08-22 00:00:00', 3.0);
答案 0 :(得分:2)
我不知道这比您的解决方案(您未显示)要快。相关子查询似乎是一个合理的解决方案。
但是另一种方法是:
SELECT j.*, b.price
FROM journeys j LEFT JOIN
(SELECT b.*, LEAD(timestamp) OVER (PARTITION BY bus_id ORDER BY timestamp) as next_timestamp
FROM bus b
) b
ON b.bus_id = j.bus AND
j.timestamp >= b.timestamp AND
(j.timestamp < b.next_timestamp OR b.next_timestamp IS NULL);
答案 1 :(得分:2)
您也可以使用内部联接和窗口函数来做到这一点:
SELECT user_id, SUM(price)
FROM
(
SELECT user_id, journey_timestamp, bus_id, price_change_timestamp,
COALESCE(LEAD(price_change_timestamp) OVER(PARTITION BY bus_id ORDER BY price_change_timestamp), CAST('2100-01-01 00:00:00' AS TIMESTAMP)) AS next_price_timestamp, price
FROM
(
SELECT a.id AS user_id, a.timestamp AS journey_timestamp, a.bus AS bus_id, b.timestamp AS price_change_timestamp, b.price
FROM journeys a
INNER JOIN bus b
ON a.bus = b.bus_id
) a1
) a2
WHERE journey_timestamp >= price_change_timestamp AND journey_timestamp < next_price_timestamp
GROUP BY user_id
这本质上是正在发生的事情:
1)内部查询连接到表中,以确保每次旅程交易都与公共汽车在所有时间点的所有票价相匹配。
2)LEAD函数按照总线票价更改时间排序的bus_id进行分区,以创建该票价对其有效的“窗口”。 COALESCE黑客将解决该过程中生成的NULL。
3)我们按旅程时间戳记位于“窗口”内的行进行过滤,并使用groupby查找每个用户的票价。