Каков наилучший способ получить связанные данные из их идентификаторов в одном запросе? - PullRequest
4 голосов
/ 27 декабря 2008

У меня есть таблица, в которой в каждой строке есть несколько полей с идентификаторами, которые относятся к некоторым другим данным из некоторых других таблиц.

Допустим, он называется people, и у каждого человека есть идентификаторы city, state и country.

Таким образом, будет еще три таблицы, cities, states и countries, где у каждой есть идентификатор и имя.

Когда я выбираю человека, какой самый простой способ получить имена из city, state и country в одном запросе?

Примечание: Я знаю, что это возможно с объединениями, однако, поскольку есть больше связанных таблиц, вложенные объединения затрудняют чтение запроса, и мне интересно, есть ли более понятный способ. Человек также должен иметь возможность оставить эти поля пустыми.

Ответы [ 10 ]

2 голосов
/ 27 декабря 2008

СОЕДИНЕНИЯ - единственный способ действительно сделать это.

Возможно, вы сможете изменить свою схему, но проблема будет одинаковой независимо.

(Город всегда находится в штате, который всегда в стране, поэтому у человека может быть просто ссылка на city_id, а не на все три. Вам все равно нужно присоединиться к 3 таблицам).

2 голосов
/ 27 декабря 2008

Предполагая следующие таблицы:

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
)
;

со следующими данными:

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);

Тогда этот запрос должен дать вам то, что вы ищете.

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
1 голос
/ 29 декабря 2008

В соответствии с описанием схемы, которую вы дали, вам придется использовать JOINS в одном запросе.

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; 

LEFT OUTER JOIN позволит вам получить данные о человеке, даже если некоторые идентификаторы будут пустыми или пустыми.

Другой способ - изменить дизайн таблиц поиска. Город всегда в государстве и государство в стране. Следовательно, ваша таблица city будет иметь столбцы: Id, Name и state_id. Ваша таблица state будет: Id, Name и country_id. И таблица country останется прежней: Id и Name.

Таблица person теперь будет иметь только 1 идентификатор: city_id

Теперь ваш запрос будет:

 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;

Обратите внимание на разницу в двух последних НАРУЖНЫХ СОЕДИНЕНИЯХ

1 голос
/ 27 декабря 2008

Нет более чистого пути, чем соединения. Если поля могут быть пустыми, используйте внешние объединения

SELECT c.*, s.name AS state_name
  FROM customer c
  LEFT OUTER JOIN state s ON s.id = c.state
 WHERE c.id = 10
0 голосов
/ 29 декабря 2008

Создайте представление, которое объединяет человека, город, штат и страну. Затем просто укажите ссылку на другие соединения.

Что-то вроде:

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

Тогда в других запросах вы можете

SELECT FullPerson.*, Other.Value  
FROM FullPerson LEFT OUTER JOIN Other ON FullPerson.OtherId = Other.Id
0 голосов
/ 29 декабря 2008

Все отличные ответы, но спрашивающий указал, что они не хотят использовать объединения. Как показал один респондент, предполагая, что в ваших таблицах Cities, States и Countries есть поля Id и Description, вы можете сделать что-то вроде этого:

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;
0 голосов
/ 27 декабря 2008

Недостатком стандартного SQL является то, что возвращаемые данные должны быть в табличном формате. Однако некоторые поставщики баз данных добавили функции, позволяющие выбирать данные в нетабличном формате. Я не знаю, знает ли MySQL такие функции.

0 голосов
/ 27 декабря 2008

Самое простое решение - использовать имена в качестве первичных ключей в city, state и country. Тогда ваша таблица person может ссылаться на них по имени вместо псевдоключа "id". Таким образом, вам не нужно делать объединения, так как ваша таблица person уже имеет необходимые значения.

Требуется больше места для хранения строки вместо 4-байтового псевдоключа. Но вы можете найти компромиссное решение, если вам угрожают такие объединения, как вам кажется (что, кстати, похоже на программиста PHP, не желающего использовать foreach - объединения фундаментальны для SQL в одном и том же способ).

Также есть много названий городов, которые встречаются в нескольких штатах. Таким образом, ваша таблица city должна ссылаться на таблицу state и использовать эти два столбца в качестве первичного ключа.

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)
);

Это всего лишь пример техники. Конечно, это сложнее, чем это, потому что у вас могут быть названия городов в более чем одной стране, у вас могут быть страны без штатов и так далее. Суть в том, что SQL не заставляет вас использовать целочисленные псевдокейки, поэтому при необходимости используйте ключи CHAR и VARCHAR.

0 голосов
/ 27 декабря 2008

Если используемые таблицы являются ссылочными таблицами (т.е. они содержат справочные данные, которые не будут изменяться в течение времени жизни сеанса), в зависимости от характера вашего приложения, вы можете предварительно загрузить справочные данные во время работы. запуск приложения. Тогда вашему запросу не нужно выполнять объединения, вместо этого он возвращает значения идентификаторов, и в вашем приложении вы делаете декодирование идентификаторов, когда вам нужно отобразить данные.

0 голосов
/ 27 декабря 2008

Соединения ответ. С практикой они станут более читабельными для вас.
Могут быть особые случаи, когда создание функции поможет вам, например, вы можете сделать следующее (в Oracle я не знаю mysql):
Вы можете создать функцию для возврата отформатированного адреса с учетом кодов города и страны, тогда ваш запрос станет

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

, где formated_address выполняет индивидуальный поиск в таблицах городов и стран и помещает разделители между декодированными значениями или возвращает "нет адреса", если они все пусты, и т. Д.

...