Domanda

Ho una tabella in cui ogni riga ha alcuni campi con ID relativi ad altri dati di altre tabelle.

Diciamo che si chiama people, e ogni persona ha l'ID di a city, state E country.

Quindi ci saranno altri tre tavoli, cities, states E countries dove ciascuno ha un ID e un nome.

Quando seleziono una persona, qual è il modo più semplice per ottenere il file nomi del city, state E country in un'unica query?

Nota: So che questo è possibile con i join, tuttavia, poiché ci sono più tabelle correlate, i join nidificati rendono la query difficile da leggere e mi chiedo se esiste un modo più semplice.Dovrebbe anche essere possibile per la persona avere questi campi vuoti.

È stato utile?

Soluzione

Supponendo che le seguenti tabelle:

create table People
(
     ID        int          not null primary key auto_increment
    ,FullName  varchar(255) not null
    ,StateID   int 
    ,CountryID int 
    ,CityID    int 
)
;
create table States
(
     ID   int          not null primary key auto_increment
    ,Name varchar(255) not null
)
;
create table Countries
(
     ID   int          not null primary key auto_increment
    ,Name varchar(255) not null
)
;
create table Cities
(
     ID   int          not null primary key auto_increment
    ,Name varchar(255) not null
)
;

Con i seguenti dati:

insert into Cities(Name) values ('City 1'),('City 2'),('City 3');
insert into States(Name) values ('State 1'),('State 2'),('State 3');
insert into Countries(Name) values ('Country 1'),('Country 2'),('Country 3');
insert into People(FullName,CityID,StateID,CountryID) values ('Has Nothing'   ,null,null,null);
insert into People(FullName,CityID,StateID,CountryID) values ('Has City'      ,   1,null,null);
insert into People(FullName,CityID,StateID,CountryID) values ('Has State'     ,null,   2,null);
insert into People(FullName,CityID,StateID,CountryID) values ('Has Country'   ,null,null,   3);
insert into People(FullName,CityID,StateID,CountryID) values ('Has Everything',   3,   2,   1);

Poi questa query dovrebbe darvi che cosa siete dopo.

select 
 P.ID
,P.FullName
,Ci.Name as CityName
,St.Name as StateName
,Co.Name as CountryName
from People P
left Join Cities    Ci on Ci.ID = P.CityID
left Join States    St on St.ID = P.StateID
left Join Countries Co on Co.ID = P.CountryID

Altri suggerimenti

si unisce sono l'unico modo per fare proprio questo.

Si potrebbe essere in grado di cambiare lo schema, ma il problema sarà lo stesso indipendentemente.

(una città è sempre in uno stato, che è sempre in un Paese -. Così la persona potrebbe semplicemente avere un riferimento al city_id piuttosto che tutti e tre Hai ancora bisogno di unire le 3 tavoli però).

Non c'è un modo più pulito di join. Se i campi sono autorizzati a essere vuoto, l'uso outer join

SELECT c.*, s.name AS state_name
  FROM customer c
  LEFT OUTER JOIN state s ON s.id = c.state
 WHERE c.id = 10

Secondo la descrizione dello schema che avete dato si dovrà utilizzare unisce a una singola query.

SELECT 
   p.first_name 
 , p.last_name
 , c.name as city
 , s.name as state
 , co.name as country 
FROM people p 
LEFT OUTER JOIN city c 
  ON p.city_id = c.id
LEFT OUTER JOIN state s 
  ON p.state_id = s.id
LEFT OUTER JOIN country co
  ON p.country_id = co.id; 

Il LEFT OUTER JOIN vi permetterà di recuperare i dettagli di persona, anche se alcuni ID sono vuoti o vuoto.

Un altro modo è quello di ridisegnare le vostre tabelle di ricerca. Una città è sempre in uno stato e uno stato in un paese. Quindi il vostro tavolo city avrà colonne: Id, Name e state_id. Il vostro tavolo state sarà: Id, Name e country_id. E tavolo country rimarrà lo stesso: Id e Name.

La tabella person ora avrà solo 1 ID: city_id

Ora la query sarà:

 SELECT 
   p.first_name 
 , p.last_name
 , c.name as city
 , s.name as state
 , co.name as country 
FROM people p 
LEFT OUTER JOIN city c 
  ON p.city_id = c.id
LEFT OUTER JOIN state s 
  ON c.state_id = s.id
LEFT OUTER JOIN country co
  ON s.country_id = co.id;

Si noti la differenza negli ultimi due outer join

Se le tabelle coinvolte sono le tabelle di riferimento (cioè in possesso dei dati di ricerca che non sta per cambiare nel corso del tempo di vita di una sessione), a seconda della natura della vostra applicazione, è possibile pre-caricare i dati di riferimento durante il vostro applicazione start up. Poi la query non ha bisogno di fare i join, ma restituisce i valori ID, e nell'applicazione e si fa una decodifica degli ID quando è necessario per visualizzare i dati.

La soluzione più semplice è quella di utilizzare i nomi, come le chiavi primarie in city, state e country. Allora la vostra tabella di person li può fare riferimento con il nome invece del pseudokey "id". In questo modo, non c'è bisogno di fare si unisce, dal momento che il tavolo person ha già i valori necessari.

Ci vuole più spazio per memorizzare una stringa invece di un pseudokey 4 byte. Ma si può trovare il compromesso vale la pena, se sono minacciati da unisce tanto quanto ti sembra di essere (che, tra l'altro, è come un programmatore PHP essere riluttanti a utilizzare foreach - si unisce sono fondamentali a SQL nello stesso modo) .

Inoltre ci sono molti nomi di città che appaiono in più di uno stato. Così il vostro tavolo city dovrebbe fare riferimento alla tabella state e utilizzare queste due colonne come chiave primaria.

CREATE TABLE cities (
  city_name VARCHAR(30),
  state     CHAR(2),
  PRIMARY KEY (city_name, state),
  FOREIGN KEY (state) REFERENCES states(state)
);

CREATE TABLE persons (
  person_id    SERIAL PRIMARY KEY,
  ...other columns...
  city_name    VARCHAR(30),
  state        CHAR(2),
  country_name VARCHAR(30),
  FOREIGN KEY (city_name, state) REFERENCES cities(city_name, state),
  FOREIGN KEY (country_name) REFERENCES countries(country_name)
);

Questo solo un esempio della tecnica. Naturalmente è più complessa di questa, perché si può avere i nomi delle città in più di un paese, si possono avere i paesi, senza stati, e così via. Il punto è SQL non ti costringe ad utilizzare pseudokeys interi, in modo da utilizzare i tasti CHAR e VARCHAR, se del caso.

Uno svantaggio dell'SQL standard è che i dati restituiti devono essere in formato tabellare.Tuttavia, alcuni fornitori di database hanno aggiunto funzionalità che rendono possibile selezionare i dati in formato non tabellare.Non so se MySQL conosca tali funzionalità.

Creare una vista che fa la persona, Città, Stato, Nazione e si unisce per voi. Poi basta fare riferimento alla vista in tutti gli altri join.

Qualcosa di simile:

CREATE VIEW FullPerson AS  
SELECT Person.*, City.Name, State.Name, Country.Name  
FROM  
  Person LEFT OUTER JOIN City ON Person.CityId = City.Id  
  LEFT OUTER JOIN State ON Person.StateId = State.Id  
  LEFT OUTER JOIN Country ON Person.CountryId = Country.Id

Poi in altre query, puoi

SELECT FullPerson.*, Other.Value  
FROM FullPerson LEFT OUTER JOIN Other ON FullPerson.OtherId = Other.Id

Tutte le grandi risposte, ma l'interrogante specificato che non volevano usare si unisce. Come uno degli intervistati ha dimostrato, supponendo che il Cities, States e tabelle Countries hanno un Id e un campo Description si potrebbe essere in grado di fare qualcosa di simile:

SELECT
   p.Name, c.Description, s.Description, ct.Description
FROM
   People p, Cities c, States s, Countries ct
WHERE
   p.Id = value AND
   c.Id = value AND
   s.Id = value AND
   ct.Id = value;

I join lo sono IL risposta.Con la pratica ti diventeranno più leggibili.
Potrebbero esserci casi speciali in cui la creazione di una funzione potrebbe aiutarti, ad esempio potresti fare quanto segue (in Oracle, non conosco nessun mysql):
Potresti creare una funzione per restituire un indirizzo formattato dati i codici stato della città e paese, quindi la tua query diventa

SELECT first_name, last_name, formated_address(city_id, state_id, country_id) 
FROM people
WHERE some_where_clause;

Dove formated_address esegue ricerche individuali sulle tabelle dello stato della città e del paese e inserisce separatori tra i valori decodificati o restituisce "nessun indirizzo" se sono tutti vuoti, ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top