Disponi di 2 tabelle, "Istituzioni" e "Aree"
Consenti a "Aree" di collegarsi a se stesso, ad esempio area_id, parent_area_id
In questo modo si collega sempre un'istituzione a un area_id, quindi la logica interna può determinare se quell'area è considerata un distretto o una città.
Quindi ora hai
institutions (
id UNSIGNED INT NOT NULL PK AI,
area_id UNSIGNED INT NOT NULL,
name VARCHAR NOT NULL
)
e
areas (
id UNSIGNED INT NOT NULL PK AI,
parent_area_id UNSIGNED INT NOT NULL DEFAULT 0,
name VARCHAR NOT NULL,
type ENUM('city','district') NOT NULL DEFAULT 'city'
)
Il campo aree.type è facoltativo ma se vuoi definirli come tali, potrebbe essere un modo per farlo all'interno del database (altrimenti supponi che se parent_area_id =0 allora è una città, altrimenti è un distretto)
In questo modo quando selezioni il campo tutto ciò che stai facendo è
SELECT *
FROM institutions
INNER JOIN areas
ON areas.id = institutions.area_id
Puoi essere sicuro al 100% a dove si collega l'area_id dell'istituzione, non c'è alcun punto interrogativo se andare alla tabella Distretti o Città, sta sicuramente andando alla tabella delle aree che a sua volta tratta i distretti e le città allo stesso modo e presenta le informazioni in un formato che il tuo front-end può quindi interpretare come città o distretto. Facoltativamente potresti fare un ulteriore passo avanti se lo desideri davvero
SELECT
i.*,
COALESCE(a_parent.id,a_child.id) AS city_id,
COALESCE(a_parent.name,a_child.name) AS city_name
FROM institutions AS i
INNER JOIN areas AS a_child
ON a_child.id = i.area_id
LEFT JOIN areas AS a_parent
ON a_parent.id = a_child.parent_area_id
Che ad esempio restituirebbe sempre il nome della città anche se l'istituto fosse legato a un distretto specifico all'interno di una città