Kvůli výkonu a za předpokladu, že používáte InnoDB, bych pravděpodobně data trochu denormalizoval, například takto:
CREATE TABLE CITY (
CITY_ID INT PRIMARY KEY
);
CREATE TABLE CITY_DISTANCE (
CITY1_ID INT,
CITY2_ID INT,
DISTANCE NUMERIC NOT NULL,
PRIMARY KEY (CITY1_ID, DISTANCE, CITY2_ID),
FOREIGN KEY (CITY1_ID) REFERENCES CITY (CITY_ID),
FOREIGN KEY (CITY2_ID) REFERENCES CITY (CITY_ID)
);
Každá dvojice měst má 2 řádky v CITY_DISTANCE obsahující stejnou DISTANCE (jeden pro každý směr). Tím by to samozřejmě mohlo být velmi velké a mohlo by to vést k nekonzistentnostem dat (databáze se nebude bránit proti neshodným hodnotám DISTANCE mezi stejnými městy) a DISTANCE logicky nepatří do PK, ale mějte se mnou...
Tabulky InnoDB jsou seskupené , což znamená, že deklarováním PK tímto konkrétním způsobem vložíme celou tabulku do B-stromu, který je zvláště vhodný pro dotaz, jako je tento:
SELECT CITY2_ID, DISTANCE
FROM CITY_DISTANCE
WHERE CITY1_ID = 1
ORDER BY DISTANCE
LIMIT 5
Tento dotaz vrátí 5 měst nejblíže k městu označenému 1
, a může být uspokojen jednoduchým skenováním rozsahu na B-stromu zmíněném výše:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE CITY_DISTANCE ref PRIMARY PRIMARY 4 const 6 "Using where; Using index"
BTW, InnoDB automaticky vytvoří jeden další index (na CITY2_ID) kvůli druhému FK, který bude zahrnovat také CITY1_ID a DISTANCE, protože sekundární indexy v klastrovaných tabulkách musí pokrývat PK. Můžete toho být schopni využít, abyste se vyhnuli duplicitním vzdálenostem (explicitně vytvořte index na {CITY2_ID, DISTANCE, CITY1_ID} a nechte jej FK znovu použít, a CHECK (CITY1_ID