构建SQL查询“自联”

时间:2015-04-23 10:30:47

标签: sql mariadb

我正在开发一个数据库,其中一个名为stop_times的表被构造为:

+-------+-------------------+---------+----------+----------+
| st_id | trip_id           | stop_id | time     | stop_seq |
+-------+-------------------+---------+----------+----------+
|     1 | 10000872820081804 |   22789 | 17:33:00 |        1 |
|     2 | 10000872820081804 |   22791 | 17:39:00 |        2 |
|     3 | 10000872820081804 |   22793 | 17:41:00 |        3 |
|     4 | 10000872820081805 |   22794 | 17:33:00 |        1 |
|     5 | 10000872820081805 |   22792 | 17:35:00 |        2 |
| [...] |       [...]       |  [...]  |   [...]  |   [...]  |
+-------+-------------------+---------+----------+----------+

stop_id可以与倍数trip_id相关联,例如:

+-------+------------------+---------+----------+----------+
| st_id | trip_id          | stop_id | time     | stop_seq |
+-------+------------------+---------+----------+----------+
| 91447 | 1017694581039141 |    1778 | 17:44:00 |       15 |
| 91599 | 1017694590917762 |    1778 | 22:40:00 |       20 |
| 91717 | 1017694610917762 |    1778 | 22:40:00 |       20 |
| 91773 | 1017694610968899 |    1778 | 16:48:00 |       15 |
| 91909 | 1017694640917762 |    1778 | 22:40:00 |       20 |
| [...] |       [...]      |   [...] |   [...]  |   [...]  |
+-------+------------------+---------+----------+----------+

给定stop_id A,我需要找到它的邻居(stop_id B),邻居被定义为:

  • A和B有共同的trip_id(例如条目1,2和3符合此条件)
  • B的
  • stop_seq必须是A的stop_seq的最小上限(通常为(+ 1)

例如, 22789 22791 是他们共享trip_id的邻居(例如, 10000872820081804 ,但他们实际上是分享了很多其他trip_id s) 22789 stop_seq为1, 22791 stop_seq为2(和2 = min({2,3})其中{2,3} = {stop_seq st trip_id = 10000872820081804和stop_seq> 1} < - top_bounds stop_seq of 22789

最后,我需要在两个停靠点之间获得时间,这是两次的减法,但我想我可以管理这个(即使我真的不知道如何在SQL中减去时间,我想你可以实现有一些SQL函数..)

还有一些相当困难的事情:从邻居那里会有很多重复(因为很多旅行会将这两个停靠点作为邻居)所以我需要找到最接近给定时间(即停止的减法)时间减去给定时间是最小的和积极的)

例如,如果SELECT * FROM stop_times WHERE stop_id = 2045返回此信息:

+-------+------------------+---------+----------+----------+
| st_id | trip_id          | stop_id | time     | stop_seq |
+-------+------------------+---------+----------+----------+
| 91421 | 1017694581039138 |    2045 | 12:01:00 |       21 |
| 91449 | 1017694581039141 |    2045 | 17:48:00 |       17 |
| 91511 | 1017694590917740 |    2045 | 10:13:00 |       21 |
| 91543 | 1017694590917746 |    2045 | 12:43:00 |       21 |
| 91601 | 1017694590917762 |    2045 | 22:43:00 |       22 |
+-------+------------------+---------+----------+----------+

(我做了LIMIT 5但我们假设它只返回那个)。然后,我知道(通过浏览数据库):

  • 对于行程 1017694581039138 下一站(stop_seq = 22这里,22是最小的数字>而不是21, 2045 的stop_seq行程) 1809 ,行程时间为5分钟(12:06 - 12:01,其中12:06是 1809 行中的时间)。
  • 对于行程 1017694581039141 ,下一站(stop_seq = 18) 1809 ,行程时间为1分钟。
  • 等。

假设我给了时间12:00,还有另一次去 1809 然后(比如说14:00),我希望查询只显示一个12:01(因为它与我给出的时间最接近)。

例如, 1017694590917740 1017694590917746 这里也会导致 1809 ,但 1017694590917740 已经过去了我给12:00作为时间, 1017694590917746 不是从12:00开始的 1017694581039138 (12:01 vs 12:40),所以唯一的旅行到 1809 我希望返回的查询是 1017694581039138

(另外,每次停止都可以这样做,但这意味着给出一个与导致每个节点的每个不同邻居的时间相关联的行程列表,如:

Node 1
+--- Neighbour 1
      +--- Time 
      +--- Other time
      +--- etc.
+--- Neighbour 2
      +--- etc.
Node 2
+--- etc.
etc.

但是这需要一种类似于json的结构,我真的不知道如何在SQL中实现

我该怎么做?

(我已尝试使用子查询和加入,但我对SQL有点不舒服,很快变得复杂,过了一会儿,我觉得我需要 foreach for 语句, if s和其他内容)

1 个答案:

答案 0 :(得分:0)

让我们只关注 next 停止,而不是 prev 停止。

鉴于您的限制,您可以使用以下方式在每次旅行中找到 next 停止:

select st.*,
       (select st2.st_id
        from stop_times st2
        where st2.trip_id = st.trip_id and st2.stop_seq > st.stop_seq
        order by st2.stop_seq
        limit 1
       ) as next_st_id
from stop_times st;

我们可以加入到表中以获取有关下一站的信息,并使用聚合来获得最短时间(以及获取其他信息的技巧):

select st.stop_id, stn.stop_id as next_stop_id,
       min(stn.time) as min_time,
       timestampdiff(second, min(stn.time), st.time) as seconds_diff,
       substring_index(group_concat(stn.st_id order by stn.time), ',', 1) as next_st_id,
       substring_index(group_concat(stn.trip_id order by stn.time), ',', 1) as next_trip_id     
from (select st.*,
             (select st2.st_id
              from stop_times st2
              where st2.trip_id = st.trip_id and st2.stop_seq > st.stop_seq
              order by st2.stop_seq
              limit 1
             ) as next_st_id
      from stop_times st
     ) st join
     stop_times stn
     on stn.st_id = st.next_st_id
group by st.stop_id, stn.stop_id ;