Possui 2 tabelas, "Instituições" e "Áreas"
Permitir que "Áreas" vinculem a si mesmas, ou seja, area_id, parent_area_id
Dessa forma, você sempre vincula uma Instituição a um area_id e, em seguida, a lógica interna pode determinar se essa área é considerada um Distrito ou uma Cidade.
Então você agora tem
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'
)
O campo areas.type é opcional, mas se você quiser defini-los como tal, pode ser uma maneira de fazer isso dentro do banco de dados (caso contrário, assuma que se parent_area_id =0, então é uma cidade, senão é um distrito)
Desta forma, ao selecionar o campo, tudo o que você está fazendo é
SELECT *
FROM institutions
INNER JOIN areas
ON areas.id = institutions.area_id
Você pode ter 100% de certeza para onde a instituição area_id está vinculada, não há dúvida sobre ir para a tabela Distritos ou Cidades, definitivamente vai para a tabela de áreas que por sua vez trata Distritos e Cidades da mesma maneira e apresenta informações em um formato que seu front-end pode interpretar como cidade ou distrito. Opcionalmente, você pode dar um passo adiante se realmente quiser
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
Isso, por exemplo, sempre retornaria o nome da cidade, mesmo que a instituição estivesse vinculada a um distrito específico dentro de uma cidade