Đối với tất cả các phân tích kinh doanh dẫn đến kiến trúc cơ sở dữ liệu, tôi khuyên bạn nên viết quy tắc:
- Một tuyến đường có 2 trạm trở lên
- Một trạm có thể được sử dụng bởi nhiều tuyến đường
- Các trạm trên một tuyến đường theo thứ tự cụ thể
Các quy tắc thứ 1 và thứ 2 như bạn nhận thấy ngụ ý rất nhiều mối quan hệ, do đó bạn đã kết luận chính xác để tạo routeStations.
Quy tắc thứ 3 là quy tắc thú vị. Nó ngụ ý rằng một cột thêm là cần thiết để phù hợp với yêu cầu. Nó nên đi đâu? Chúng ta có thể thấy rằng khách sạn này phụ thuộc vào Tuyến VÀ Trạm. Do đó, nó nên được đặt trong routeStations.
Tôi sẽ thêm một cột vào bảng RouteStations được gọi là "trạm".
+-------------+---------------+---------------
| routeId(fk) | stationId(fk) | StationOrder |
+-------------+---------------+---------------
| 1 | 1 | 3 |
+-------------+---------------+---------------
| 1 | 3 | 1 |
+-------------+---------------+---------------
| 1 | 4 | 2 |
+-------------+---------------+---------------
| 2 | 1 | 1 |
+-------------+---------------+---------------
| 2 | 4 | 2 |
+-------------+---------------+---------------
Sau đó, truy vấn trở nên dễ dàng:
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 |
+-------------+---------------+
Ghi chú:
- Tôi đã sửa lỗi StationId trong RouteStations trong ví dụ của mình. Bạn đang sử dụng StationName làm Id.
- Nếu bạn không sử dụng tên tuyến đường, thì thậm chí không cần tuyến đường vì bạn có thể lấy tên đó từ routeStations
- Ngay cả khi bạn sẽ liên kết với bảng lộ trình, trình tối ưu hóa cơ sở dữ liệu của bạn sẽ nhận thấy rằng nó không cần liên kết bổ sung đó và chỉ cần loại bỏ các bước bổ sung.
Để phát triển trên lưu ý 3, tôi đã xây dựng trường hợp sử dụng:
Đây là doanh nghiệp Oracle 12c.
Lưu ý rằng trong kế hoạch thực hiện bên dưới, các tuyến bảng không được sử dụng. Trình tối ưu hóa cơ sở chi phí (CBO) biết rằng nó có thể lấy tuyếnId trực tiếp từ khóa chính của routeStations (bước 5, INDEX RANGE SCAN trên ROUTESTATION_PK, Thông tin dự đoán 5 - quyền truy cập ("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")
Bây giờ là phần thú vị, hãy thêm một tên cột vào bảng tuyến đường. Bây giờ có một cột chúng ta thực sự cần trong "tuyến đường". CBO sử dụng chỉ mục để tìm rowID cho tuyến 1, sau đó truy cập vào bảng (truy cập bảng theo chỉ mục rowid) và lấy cột "Rout.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")