在数据库中存储公交路线


16

我进行了一些研究,发现应该将路线存储为停靠点序列。就像是:

Start -> Stop A -> Stop B -> Stop C -> End

我创建了三个表:

  • 路线
  • 停止
  • 路线停靠点

...其中RouteStops是联结表。

我有类似的东西:

路线

+---------+
| routeId |
+---------+
|    1    |
+---------+
|    2    |
+---------+

车站

+-----------+------+
| stationId | Name |
+-----------+------+
|     1     |   A  |
+-----------+------+
|     2     |   B  |
+-----------+------+
|     3     |   C  |
+-----------+------+
|     4     |   D  |
+-----------+------+

路线站

+-------------+---------------+
| routeId(fk) | stationId(fk) |
+-------------+---------------+
|     1       |       A       |
+-------------+---------------+
|     1       |       C       |
+-------------+---------------+
|     1       |       D       |
+-------------+---------------+
|     2       |       A       |
+-------------+---------------+
|     2       |       D       |
+-------------+---------------+

路线1经过

Station A -> Station C -> Station D

路线2经过

Station A -> Station D

这是存储路线的好方法吗?

根据维基百科

数据库系统不保证行的任何顺序,除非ORDER BY指定了子句[...]

我可以依靠这样的数据库模式,还是应该以不同的方式来做?

这实际上是我的大学项目,所以我想知道这样的模式是否可以视为正确的模式。对于这种情况,我可能只存储几条路线(大约3-5条)和车站(大约10-15条),每条路线将包含大约5个站。我也很高兴听到在真正的大型巴士公司的情况下的情况。


您可能需要查看通用公交提要规范;尽管指定GTFS供稿以CSV文件形式进行交换,但应用程序通常会在关系数据库中存储和操作GTFS。
Kurt Raschke

3
您的问题在术语“停止”和“站”之间切换。您可能应该澄清您的域词汇(选择一个名称并保持不变)。
Tersosauros's

@ monoh_.i也有类似的问题dba.stackexchange.com/questions/194223/…。如果您有想法可以分享
愿景

Answers:


19

对于所有导致数据库体系结构的业务分析,我建议编写规则:

  • 一条路线有2个或更多的车站
  • 一个车站可以被许多路线使用
  • 路线上的车站按特定顺序排列

您注意到的第一和第二条规则意味着多对多关系,因此您正确地总结了创建routeStation的信息。

第三条规则很有趣。这意味着需要额外的一列才能满足要求。应该去哪里?我们可以看到此属性取决于Route AND St​​ation。因此,它应该位于routeStations中。

我将在表routeStations中添加一列,称为“ stationOrder”。

+-------------+---------------+---------------
| routeId(fk) | stationId(fk) | StationOrder |
+-------------+---------------+---------------
|     1       |       1       |       3      |
+-------------+---------------+---------------
|     1       |       3       |       1      |
+-------------+---------------+---------------
|     1       |       4       |       2      |
+-------------+---------------+---------------
|     2       |       1       |       1      |
+-------------+---------------+---------------
|     2       |       4       |       2      |
+-------------+---------------+---------------

然后查询变得容易:

select rs.routeID,s.Name
from routeStations rs
join
Stations s
on rs.stationId=s.StationId
where rs.routeId=1
order by rs.StationOrder;

+-------------+---------------+
| routeId(fk) | stationId(fk) |
+-------------+---------------+
|     1       |       C       |
+-------------+---------------+
|     1       |       D       |
+-------------+---------------+
|     1       |       A       |
+-------------+---------------+

笔记:

  1. 我在示例中修复了RouteStation中的StationId。您正在使用StationName作为ID。
  2. 如果您不使用路线名称,则甚至不需要routeId,因为您可以从routeStations获取该名称。
  3. 即使您链接到路由表,数据库优化器也会注意到它不需要该额外链接,只需删除这些额外步骤。

为了在注释3上进行开发,我构建了用例:

这是Oracle 12c企业版。

请注意,在下面的执行计划中,根本没有使用表路由。Cost Base Optimizer(CBO)知道它可以直接从routeStations的主键获取routeId(步骤5,ROUTESTATIONS_PK上的INDEX RANGE SCAN,谓词信息5-access(“ RS”。“ ROUTEID” = 1))

--Table ROUTES
create sequence routeId_Seq start with 1 increment by 1 maxvalue 9999999999999 cache 1000;

CREATE TABLE routes
(
  routeId  INTEGER NOT NULL
);


ALTER TABLE routes ADD (
  CONSTRAINT routes_PK
  PRIMARY KEY
  (routeId)
  ENABLE VALIDATE);

insert into routes values (routeId_Seq.nextval);
insert into routes values (routeId_Seq.nextval);
commit;

--TABLE STATIONS  
create sequence stationId_seq start with 1 increment by 1 maxvalue 9999999999999 cache 1000;

create table stations(
   stationID INTEGER NOT NULL,
   name varchar(50) NOT NULL
);

ALTER TABLE stations ADD (
  CONSTRAINT stations_PK
  PRIMARY KEY
  (stationId)
  ENABLE VALIDATE);

insert into stations values (stationId_seq.nextval,'A');
insert into stations values (stationId_seq.nextval,'B');
insert into stations values (stationId_seq.nextval,'C');
insert into stations values (stationId_seq.nextval,'D');
commit;
--

--Table ROUTESTATIONS 
CREATE TABLE routeStations
(
  routeId       INTEGER NOT NULL,
  stationId     INTEGER NOT NULL,
  stationOrder  INTEGER NOT NULL
);


ALTER TABLE routeStations ADD (
  CONSTRAINT routeStations_PK
  PRIMARY KEY
  (routeId, stationId)
  ENABLE VALIDATE);

ALTER TABLE routeStations ADD (
  FOREIGN KEY (routeId) 
  REFERENCES ROUTES (ROUTEID)
  ENABLE VALIDATE,
  FOREIGN KEY (stationId) 
  REFERENCES STATIONS (stationId)
  ENABLE VALIDATE);

insert into routeStations values (1,1,3);
insert into routeStations values (1,3,1);
insert into routeStations values (1,4,2);
insert into routeStations values (2,1,1);
insert into routeStations values (2,4,2);
commit;

explain plan for select rs.routeID,s.Name
from ndefontenay.routeStations rs
join
ndefontenay.routes r
on r.routeId=rs.routeId
join ndefontenay.stations s
on rs.stationId=s.stationId
where rs.routeId=1
order by rs.StationOrder;

set linesize 1000
set pages 500
select * from table (dbms_xplan.display);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 2617709240                                                                                                                                                                                                                                                                                 

---------------------------------------------------------------------------------------------------                                                                                                                                                                                                         
| Id  | Operation                      | Name             | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                                                                                                                         
---------------------------------------------------------------------------------------------------                                                                                                                                                                                                         
|   0 | SELECT STATEMENT               |                  |     1 |    79 |     1 (100)| 00:00:01 |                                                                                                                                                                                                         
|   1 |  SORT ORDER BY                 |                  |     1 |    79 |     1 (100)| 00:00:01 |                                                                                                                                                                                                         
|   2 |   NESTED LOOPS                 |                  |       |       |            |          |                                                                                                                                                                                                         
|   3 |    NESTED LOOPS                |                  |     1 |    79 |     0   (0)| 00:00:01 |                                                                                                                                                                                                         
|   4 |     TABLE ACCESS BY INDEX ROWID| ROUTESTATIONS    |     1 |    39 |     0   (0)| 00:00:01 |                                                                                                                                                                                                         
|*  5 |      INDEX RANGE SCAN          | ROUTESTATIONS_PK |     1 |       |     0   (0)| 00:00:01 |                                                                                                                                                                                                         
|*  6 |     INDEX UNIQUE SCAN          | STATIONS_PK      |     1 |       |     0   (0)| 00:00:01 |                                                                                                                                                                                                         
|   7 |    TABLE ACCESS BY INDEX ROWID | STATIONS         |     1 |    40 |     0   (0)| 00:00:01 |                                                                                                                                                                                                         
---------------------------------------------------------------------------------------------------                                                                                                                                                                                                         

Predicate Information (identified by operation id):                                                                                                                                                                                                                                                         
---------------------------------------------------                                                                                                                                                                                                                                                         

   5 - access("RS"."ROUTEID"=1)                                                                                                                                                                                                                                                                             
   6 - access("RS"."STATIONID"="S"."STATIONID")

现在有趣的部分,让我们在路由表中添加一个列名。现在在“路线”中实际需要一列。CBO使用索引查找路由1的rowID,然后访问表(通过索引rowid进行表访问)并获取列“ routes.name”。

ALTER TABLE ROUTES
 ADD (name  VARCHAR2(50));

update routes set name='Old Town' where routeId=1;
update routes set name='North County' where routeId=2;
commit;

explain plan for select r.name as routeName,s.Name as stationName
from routeStations rs
join
routes r
on r.routeId=rs.routeId
join stations s
on rs.stationId=s.stationId
where rs.routeId=1
order by rs.StationOrder;

set linesize 500
set pages 500
select * from table (dbms_xplan.display);

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                           
---------------------------------------------------------------------------------------------------
Plan hash value: 3368128430                                                                                                                                                                                                                                                                                 

----------------------------------------------------------------------------------------------------                                                                                                                                                                                                        
| Id  | Operation                       | Name             | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                                                                                                                        
----------------------------------------------------------------------------------------------------                                                                                                                                                                                                        
|   0 | SELECT STATEMENT                |                  |     1 |   119 |     1 (100)| 00:00:01 |                                                                                                                                                                                                        
|   1 |  SORT ORDER BY                  |                  |     1 |   119 |     1 (100)| 00:00:01 |                                                                                                                                                                                                        
|   2 |   NESTED LOOPS                  |                  |       |       |            |          |                                                                                                                                                                                                        
|   3 |    NESTED LOOPS                 |                  |     1 |   119 |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|   4 |     NESTED LOOPS                |                  |     1 |    79 |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|   5 |      TABLE ACCESS BY INDEX ROWID| ROUTES           |     1 |    40 |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|*  6 |       INDEX UNIQUE SCAN         | ROUTES_PK        |     1 |       |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|   7 |      TABLE ACCESS BY INDEX ROWID| ROUTESTATIONS    |     1 |    39 |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|*  8 |       INDEX RANGE SCAN          | ROUTESTATIONS_PK |     1 |       |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|*  9 |     INDEX UNIQUE SCAN           | STATIONS_PK      |     1 |       |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
|  10 |    TABLE ACCESS BY INDEX ROWID  | STATIONS         |     1 |    40 |     0   (0)| 00:00:01 |                                                                                                                                                                                                        
----------------------------------------------------------------------------------------------------                                                                                                                                                                                                        

Predicate Information (identified by operation id):                                                                                                                                                                                                                                                         
---------------------------------------------------                                                                                                                                                                                                                                                         

   6 - access("R"."ROUTEID"=1)                                                                                                                                                                                                                                                                              
   8 - access("RS"."ROUTEID"=1)                                                                                                                                                                                                                                                                             
   9 - access("RS"."STATIONID"="S"."STATIONID")      

@ Nicolas.i也有类似的问题,你能帮我吗dba.stackexchange.com/questions/194223/…–
愿景

3

您是对的,关系表中没有记录的固有顺序。这意味着您需要提供一些明确的方式来订购每个路线内的站点。

根据您计划访问数据的方式,您可以

  1. sequenceNumber列添加到RouteStations显然,存储每个路径中每个站点的序列。
  2. 添加该nextStationId列以将“指针”存储到每个路由中的下一个站点。

@ mustaccio.i也有类似的问题,你能帮我吗dba.stackexchange.com/questions/194223/…–
愿景

0

我没有看到任何人对此表示任何意见,所以我想为您的成绩加分。我还将在所有三列的RouteStations / RouteStops表上放置一个非聚集的唯一索引(取决于您的RDBMS)。这样,您将不会犯错误,并使公共汽车前往2个下一个车站。这将使更新变得更加困难,但我认为仍应将其视为良好设计的一部分。


-1

我以应用程序程序员的身份发言:

甚至不用考虑对数据库(或在存储的proc中)进行查询的路由或计时,它永远不会足够快。(除非这只是一个“作业”问题。

即使对于处理内存中数据的应用程序,从数据库中加载数据也永远不会很快,除非所有数据在启动时都已加载,或者数据以不合理的形式存储。一旦数据被废止,使用关系数据库就毫无意义。

因此,我会将数据库视为数据的“主”副本,并接受我还必须将其预处理存储在应用程序内存中,或存储在诸如membase的现金服务器中。

ndefontenay的答案提供了一个很好的表格设计作为起点,但是您必须考虑到路线根据一天中的时间而有所不同,并且通常会根据时间,星期几甚至是学校假期而有不同的停靠点。


5
他无处提及自己想做路由或时间表。他问如何在DB中存储路由。此外,虽然程序员可能会士气低落,但我希望在某些时候将数据(去规范化)。:)
AnoE
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.