データベースへのバスルートの保存


16

いくつかの調査を行った結果、ルートを一連のストップとして保存する必要があることがわかりました。何かのようなもの:

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

3つのテーブルを作成しました。

  • ルート
  • 止まる
  • ルートストップ

... 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を保存および操作します。
カートラシュケ

3
質問は、「停止」と「駅」という用語を切り替えます。ドメインのボキャブラリーを明確にする必要があります(つまり、名前を1つ選択してそのままにします)。
テルソサウルス

@ monoh_.iにも同様の質問がありますdba.stackexchange.com/questions/194223/…。アイデアがあれば共有できますか
ビジョン

回答:


19

データベースアーキテクチャに至るすべてのビジネス分析について、ルールを記述することをお勧めします。

  • ルートには2つ以上のステーションがあります
  • ステーションは多くのルートで使用できます
  • ルート上の駅は特定の順序で到着します

お気づきのように、1番目と2番目のルールは多対多の関係を意味するため、routeStationsを作成するために正当な結論を下しました。

3番目のルールは興味深いものです。これは、要件を満たすために追加の列が必要であることを意味します。どこに行くべきですか?このプロパティはRoute AND St​​ationに依存していることがわかります。したがって、routeStationsに配置する必要があります。

「stationOrder」というrouteStationsテーブルに列を追加します。

+-------------+---------------+---------------
| 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. この例では、RouteStationsのStationIdを修正しました。IdとしてStationNameを使用しています。
  2. ルート名を使用しない場合、routeStationsから取得できるため、routeIdも必要ありません。
  3. ルートテーブルにリンクする場合でも、データベースオプティマイザーは、その余分なリンクが必要ないことに気付き、余分な手順を単に削除します。

注3で開発するために、ユースケースを作成しました。

これはOracle 12c Enterpriseです。

以下の実行計画では、テーブルルートはまったく使用されないことに注意してください。コストベースオプティマイザー(CBO)は、routeStationのプライマリキーから直接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

誰もこれについて何も述べていないので、あなたの成績に追加すると思いました。また、3つの列すべてにわたってRouteStations / RouteStopsテーブルに非クラスター化一意インデックス(RDBMSに依存)を配置します。この方法では、間違いを犯すことができず、バスを次の2つの駅に行くことができません。これにより、更新が難しくなりますが、それでも良いデザインの一部と見なすべきです。


-1

私はアプリケーションプログラマーとして話しています

データベース(またはストアドプロシージャ)に対するクエリを使用してルーティングや時間割を行うことも考えないでください。十分に高速になることはありません。(これが単なる「宿題」問題でない限り。

メモリ内のデータを処理するアプリケーションであっても、データベースからデータをロードすることは、すべてのデータが起動時にロードされるか、データが意気消沈した形式で保存されない限り高速にはなりません。データの士気が低下すると、リレーショナルデータベースを使用する意味はほとんどありません。

したがって、データベースは「マスター」コピーである考えますはデータのであると考え、アプリケーションメモリまたはmembaseなどのキャッシングサーバーに前処理して保存する必要があることを受け入れます。

ndefontenayの答えは出発点として適切なテーブルデザインを提供しますが、ルートは時刻に応じてタイミングが異なり、多くの場合、時間、曜日、または学校の休暇に応じて停車地が異なることを考慮する必要があります。


5
彼はルーティングや時間割をやりたいと言っているところはどこにもありません。彼は、ルートをDB に保存する方法を尋ねます。それに、プログラマーは士気を低下させるかもしれませんが、ある時点でデータが(非)正規化されることを確信しています。:)
AnoE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.