สำหรับการวิเคราะห์ธุรกิจทั้งหมดที่นำไปสู่สถาปัตยกรรมฐานข้อมูลฉันขอแนะนำให้เขียนกฎ:
- เส้นทางมี 2 สถานีขึ้นไป
- สามารถใช้สถานีได้หลายเส้นทาง
- สถานีบนเส้นทางมาในลำดับที่เฉพาะเจาะจง
กฎข้อที่ 1 และ 2 ตามที่คุณสังเกตเห็นถึงความสัมพันธ์หลายต่อหลายเพื่อให้คุณสรุปได้อย่างถูกต้องในการสร้างเส้นทาง
กฎข้อที่ 3 เป็นกฎที่น่าสนใจ แสดงว่าจำเป็นต้องมีคอลัมน์พิเศษเพื่อให้เหมาะกับความต้องการ มันควรจะไปที่ไหน เราจะเห็นว่าคุณสมบัตินี้ขึ้นอยู่กับเส้นทางและสถานี ดังนั้นจึงควรอยู่ใน routeStations
ฉันจะเพิ่มคอลัมน์ลงในตารางเส้นทาง Stations เรียกว่า "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 |
+-------------+---------------+
หมายเหตุ:
- ฉันแก้ไข StationId ใน RouteStations ในตัวอย่างของฉัน คุณกำลังใช้ชื่อสถานีเป็นรหัส
- หากคุณไม่ได้ใช้ชื่อเส้นทางดังนั้นคุณไม่จำเป็นต้องมีรหัสเส้นทางเลยเพราะคุณสามารถหาได้จากเส้นทางเส้นทาง
- แม้ว่าคุณจะเชื่อมโยงไปยังตารางเส้นทางเครื่องมือเพิ่มประสิทธิภาพฐานข้อมูลของคุณจะสังเกตเห็นว่ามันไม่จำเป็นต้องมีลิงก์พิเศษนั้นและเพียงแค่ลบขั้นตอนเพิ่มเติมออกไป
เพื่อพัฒนาในหมายเหตุ 3 ฉันได้สร้างกรณีการใช้งาน:
นี่คือ Oracle 12c Enterprise
โปรดทราบว่าในแผนการดำเนินการด้านล่างนั้นไม่มีการใช้เส้นทางตารางเลย เครื่องมือเพิ่มประสิทธิภาพฐานต้นทุน (CBO) รู้ว่าสามารถรับรหัสประจำตัวได้โดยตรงจากคีย์หลักของ RouteStations (ขั้นตอนที่ 5, INDEX RANGE SCAN บน ROUTESTATIONS_PK, ข้อมูลคำอธิบาย 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")
ตอนนี้ความสนุกมาเพิ่มชื่อคอลัมน์ในตารางเส้นทาง ตอนนี้มีคอลัมน์ที่เราต้องการใน "เส้นทาง" จริง ๆ แล้ว CBO ใช้ดัชนีเพื่อค้นหา rowID สำหรับเส้นทาง 1 จากนั้นเข้าถึงตาราง (การเข้าถึงตารางโดยดัชนี rowid) และคว้าคอลัมน์ "route.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")