SQL:在第一个匹配的行条件下联接2个表

时间:2018-08-22 21:40:39

标签: sql postgresql

我有两个正确的表

用户旅程

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

2 个答案:

答案 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查找每个用户的票价。