Menyimpan rute bus dalam database

16

Saya telah melakukan riset dan menemukan bahwa saya harus menyimpan rute sebagai urutan pemberhentian. Sesuatu seperti:

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

Saya telah membuat tiga tabel:

  • Rute
  • Berhenti
  • RouteStops

... di mana RouteStops adalah tabel persimpangan.

Saya punya sesuatu seperti:

Rute

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

Stasiun

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

RouteStations

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

Rute 1 melewati

Station A -> Station C -> Station D

Rute 2 melewati

Station A -> Station D

Apakah ini cara yang baik untuk menyimpan rute?

Menurut Wikipedia :

[...] sistem database tidak menjamin pemesanan baris kecuali jika ORDER BYklausa ditentukan [...]

Bisakah saya mengandalkan skema database seperti itu atau mungkin ini harus dilakukan secara berbeda?

Ini sebenarnya proyek universitas saya, jadi saya hanya ingin tahu apakah skema seperti itu dapat dianggap sebagai yang benar. Untuk kasus ini, saya mungkin akan menyimpan hanya beberapa rute (sekitar 3-5) dan stasiun (sekitar 10-15), setiap rute akan terdiri dari sekitar 5 stasiun. Saya juga akan senang mendengar bagaimana ini akan terlihat dalam kasus perusahaan bus nyata dan besar.

monoh_
sumber
Anda mungkin ingin melihat Spesifikasi Umpan Transit Umum ; sementara umpan GTFS ditentukan untuk ditukar sebagai file CSV, aplikasi sering menyimpan dan memanipulasi GTFS dalam database relasional.
Kurt Raschke
3
Pertanyaan Anda beralih di antara istilah 'Stop' dan 'Station'. Anda mungkin harus mengklarifikasi kosakata domain Anda ( yaitu memilih satu nama dan tetap menggunakannya).
Tersosauros
@ monoh_.i juga memiliki pertanyaan serupa dba.stackexchange.com/questions/194223/... .jika Anda memiliki gagasan, bisakah Anda membagikan
visi

Jawaban:

19

Untuk semua analisis bisnis yang mengarah ke arsitektur basis data, saya merekomendasikan aturan penulisan:

  • Rute memiliki 2 stasiun atau lebih
  • Stasiun dapat digunakan oleh banyak rute
  • Stasiun pada rute datang dalam urutan tertentu

Aturan 1 dan 2 seperti yang Anda perhatikan menyiratkan hubungan banyak ke banyak sehingga Anda menyimpulkan dengan benar untuk membuat routeStations.

Aturan ke-3 adalah aturan yang menarik. Ini menyiratkan bahwa kolom tambahan diperlukan untuk memenuhi persyaratan. Kemana harus pergi? Kita dapat melihat bahwa properti ini tergantung pada Rute DAN Stasiun. Oleh karena itu harus ditempatkan di routeStations.

Saya akan menambahkan kolom ke tabel routeStations yang disebut "stationOrder".

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

Maka kueri menjadi mudah:

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       |
+-------------+---------------+

Catatan:

  1. Saya memperbaiki StationId di RouteStations dalam contoh saya. Anda menggunakan StationName sebagai Id.
  2. Jika Anda tidak menggunakan nama rute, maka bahkan tidak perlu untuk routeId karena Anda bisa mendapatkannya dari routeStations
  3. Bahkan jika Anda akan menautkan ke tabel rute, pengoptimal database Anda akan melihat itu tidak memerlukan tautan tambahan dan cukup menghapus langkah-langkah tambahan.

Untuk mengembangkan pada catatan 3, saya telah membangun use case:

Ini adalah Oracle 12c Enterprise.

Perhatikan bahwa dalam rencana eksekusi di bawah ini, rute tabel tidak digunakan sama sekali. Pengoptimal Basis Biaya (CBO) tahu itu bisa mendapatkan routeId langsung dari kunci primer routeStations (langkah 5, INDEX RANGE SCAN pada ROUTESTATIONS_PK, Informasi Predikat 5 - akses ("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")

Sekarang bagian yang menyenangkan, mari kita tambahkan nama kolom ke tabel rute. Sekarang ada kolom yang sebenarnya kita butuhkan di "rute". CBO menggunakan indeks untuk menemukan rowID untuk rute 1, kemudian mengakses tabel (akses tabel dengan indeks rowid) dan mengambil kolom "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 de Fontenay
sumber
@ Nicolas.i juga memiliki pertanyaan serupa, bisakah Anda membantu saya dba.stackexchange.com/questions/194223/…
vision
3

Anda benar, tidak ada urutan rekaman yang melekat dalam tabel relasional. Ini berarti Anda perlu memberikan cara pemesanan stasiun secara eksplisit dalam setiap rute.

Bergantung pada bagaimana Anda berencana mengakses data yang Anda bisa

  1. Tambahkan sequenceNumberkolom ke RouteStationsuntuk menyimpan, jelas, urutan masing-masing stasiun di setiap rute.
  2. Tambahkan nextStationIdkolom untuk menyimpan "pointer" ke stasiun berikutnya di setiap rute.
mustaccio
sumber
@ mustaccio.i juga punya pertanyaan serupa, bisakah Anda membantu saya dba.stackexchange.com/questions/194223/…
vision
0

Saya tidak melihat ada yang menyatakan tentang hal ini jadi saya pikir saya akan menambah nilai Anda. Saya juga menempatkan indeks Unik yang tidak berkerumun (tergantung pada RDBMS Anda) pada tabel RouteStations / RouteStops di ketiga kolom. Dengan cara ini Anda tidak akan dapat membuat kesalahan dan meminta bus pergi ke 2 stasiun berikutnya. Ini akan mempersulit pembaruan, tetapi saya pikir masih harus dipertimbangkan sebagai bagian dari desain yang bagus.

Josh Simar
sumber
-1

Saya berbicara sebagai pemrogram aplikasi :

Jangan pernah berpikir untuk melakukan perutean atau penjadwalan dengan pertanyaan terhadap basis data (atau dalam proc yang disimpan) itu tidak akan pernah cukup cepat. ( Kecuali ini hanya masalah "pekerjaan rumah". )

Bahkan untuk aplikasi yang memproses data dalam memori memuat data dari database tidak akan pernah cepat kecuali semua data dimuat saat start up, atau data disimpan dalam bentuk demoralisasi. Setelah data mengalami demoralisasi, tidak ada gunanya menggunakan database relasional.

Oleh karena itu saya akan menganggap database sebagai salinan "master" dari data dan menerima bahwa saya juga harus menyimpannya pra-diproses dalam memori aplikasi, atau di server cashing seperti membase.

Jawaban ndefontenay memberikan desain tabel yang baik sebagai titik awal, tetapi Anda harus mempertimbangkan bahwa rute memiliki waktu yang berbeda tergantung pada waktu hari dan sering memiliki pemberhentian yang berbeda tergantung pada waktu, hari dalam seminggu, atau bahkan liburan sekolah.

Ian Ringrose
sumber
5
Tidak ada di mana pun dia menyebutkan bahwa dia ingin melakukan routing atau penjadwalan; dia bertanya bagaimana cara menyimpan rute dalam DB. Selain itu, sementara seorang programmer mungkin mengalami demoralisasi, saya yakin data tersebut akan (dinon- normalisasi ) di beberapa titik. :)
AnoE