데이터베이스에 버스 경로 저장


16

나는 약간의 연구를했으며 경로를 일련의 정류장으로 저장해야한다는 것을 발견했습니다. 다음과 같은 것 :

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

세 개의 테이블을 만들었습니다.

  • 노선
  • 중지
  • RouteStops

... 여기서 RouteStops 는 접합 테이블입니다.

나는 다음과 같은 것을 가지고있다 :

노선

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

방송국

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

루트 1이 통과

Station A -> Station C -> Station D

2 번 도로 통과

Station A -> Station D

경로를 저장하는 좋은 방법입니까?

Wikipedia 에 따르면 :

[...] 데이터베이스 시스템은 ORDER BY절이 지정 되지 않으면 행의 순서를 보증하지 않습니다 [...]

이러한 데이터베이스 스키마에 의존 할 수 있습니까 아니면 다르게 수행해야합니까?

이것은 실제로 내 대학 프로젝트이므로 그러한 스키마가 올바른 것으로 간주 될 수 있는지 궁금합니다. 이 경우 아마도 몇 개의 경로 (약 3-5)와 스테이션 (약 10-15) 만 저장하고 각 경로는 약 5 개의 스테이션으로 구성됩니다. 또한 실제 대형 버스 회사의 경우 이것이 어떻게 보이는지 알게되어 기쁩니다.


당신은 일반 대중 교통 피드 사양 을 볼 수 있습니다 ; GTFS 피드는 CSV 파일로 교환되도록 지정되는 반면, 애플리케이션은 종종 관계형 데이터베이스에 GTFS를 저장하고 조작합니다.
Kurt Raschke

3
귀하의 질문은 '중지'와 '스테이션'이라는 용어 사이에서 전환됩니다. 도메인 어휘를 명확히해야합니다 ( 예 : 하나의 이름을 선택하고 그대로 유지).
Tersosauros

@ monoh_.i도 비슷한 종류의 질문 dba.stackexchange.com/questions/194223/…. 아이디어가 있다면 공유 할 수 있습니다
vision

답변:


19

데이터베이스 아키텍처로 이어지는 모든 비즈니스 분석에 대해 규칙을 작성하는 것이 좋습니다.

  • 경로에는 2 개 이상의 스테이션이 있습니다
  • 많은 노선에서 역을 이용할 수 있습니다
  • 노선의 역은 특정 순서로옵니다

알다시피 첫 번째 규칙과 두 번째 규칙은 다 대다 관계를 의미하므로 routeStations를 올바르게 작성하기로 결론을 내 렸습니다.

세 번째 규칙은 흥미로운 규칙입니다. 요구 사항에 맞게 추가 열이 필요하다는 것을 의미합니다. 어디로 가야합니까? 이 속성은 Route AND Station에 따라 다릅니다. 따라서 routeStations에 있어야합니다.

"stationOrder"라는 테이블 routeStation에 열을 추가합니다.

+-------------+---------------+---------------
| 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를 수정했습니다. StationName을 ID로 사용하고 있습니다.
  2. 경로 이름을 사용하지 않으면 routeStations에서 경로 이름을 얻을 수 있으므로 routeId가 필요하지 않습니다.
  3. 라우팅 테이블에 연결하더라도 데이터베이스 최적화 프로그램은 추가 링크가 필요하지 않고 추가 단계를 간단히 제거 할 수 있습니다.

노트 3에서 개발하기 위해 유스 케이스를 작성했습니다.

Oracle 12c Enterprise입니다.

아래의 실행 계획에서 해당 테이블 경로는 전혀 사용되지 않습니다. 비용 기반 최적화 프로그램 (CBO)은 routeStation의 기본 키에서 직접 routeId를 가져올 수 있음을 알고 있습니다 (5 단계, ROUTESTATIONS_PK의 INDEX RANGE SCAN, 술어 정보 5-액세스 ( "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")

이제 재미있는 부분은 라우팅 테이블에 열 이름을 추가하겠습니다. 이제 "routes"에 실제로 필요한 열이 있습니다. 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/…
vision

3

관계형 테이블에는 고유 한 레코드 순서가 없습니다. 즉, 각 경로 내에서 스테이션을 명시 적으로 주문하는 방법을 제공해야합니다.

데이터에 액세스하려는 방법에 따라

  1. 각 경로에 각 스테이션의 시퀀스를 저장 하려면 sequenceNumber열을 추가하십시오 RouteStations.
  2. nextStationId각 경로의 다음 스테이션에 "포인터"를 저장하기 위해 열을 추가하십시오 .

@ mustaccio.i도 비슷한 종류의 질문이 있습니다. dba.stackexchange.com/questions/194223/…
vision

0

나는 아무도 이것에 대해 아무것도 언급하지 않았기 때문에 나는 당신의 학년에 추가 할 것이라고 생각했습니다. 또한 세 열 모두의 RouteStations / RouteStops 테이블에 클러스터되지 않은 고유 인덱스 (RDBMS에 따라 다름)를 배치했습니다. 이렇게하면 실수를 할 수없고 버스가 2 개의 다음 역으로 갈 수 있습니다. 이렇게하면 업데이트가 어려워 지지만 여전히 좋은 디자인의 일부로 간주되어야한다고 생각합니다.


-1

응용 프로그램 프로그래머 로 말하고 있습니다 .

심지어 데이터베이스 (또는 저장된 proc)에 대한 쿼리로 라우팅 또는 타임 테이블을 작성한다고 생각조차하지 마십시오. ( 이것이 단지“숙제”문제가 아닌 한 )

메모리에서 데이터를 처리하는 응용 프로그램의 경우에도 데이터베이스에서 데이터를로드 할 때 모든 데이터가 시작될 때로드되거나 데이터가 민첩 화 된 형식으로 저장되지 않는 한 빠르지 않습니다. 일단 데이터가 강등되면 관계형 데이터베이스를 사용하는 데 별다른 의미가 없습니다.

따라서 데이터베이스를 데이터의 "마스터"복사본 으로 생각하고 응용 프로그램 메모리 또는 membase와 같은 캐싱 서버에 사전 처리 된 데이터베이스 를 저장해야한다는 점에 동의합니다.

ndefontenay의 대답은 좋은 테이블 디자인을 시작점으로 제공하지만 경로는 시간에 따라 시간이 다르고 시간, 요일 또는 학교 휴일에 따라 다른 정류장이 있다는 것을 고려해야합니다.


5
그가 라우팅이나 타임 타 블링을 원한다고 언급 한 곳은 없다. 그는 DB에 경로 를 저장 하는 방법을 묻습니다 . 또한 프로그래머가 사기를 겪을 수는 있지만 데이터가 어느 시점에서 정규화 되기를 바랍니다 . :)
AnoE
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.