4

我有一个表,其中每一行都有一些字段,这些字段的 ID 与其他表中的一些其他数据相关。

假设它被称为people,每个人的 ID 为acity和。statecountry

所以会有另外三个表,,cities每个表都有一个 ID 和一个名称。statescountries

当我选择一个人时,在单个查询中获取,和的名称的最简单方法是什么?citystatecountry

注意:我知道这可以通过连接实现,但是由于有更多相关表,嵌套连接使查询难以阅读,我想知道是否有更简洁的方法。该人也应该可以将这些字段留空。

4

10 回答 10

2

JOINS 是真正做到这一点的唯一方法。

您也许可以更改您的架构,但无论如何问题都是一样的。

(一个城市总是在一个州,它总是在一个国家 - 所以这个人可以只引用 city_id 而不是全部三个。你仍然需要加入 3 个表)。

于 2008-12-27T07:20:23.733 回答
2

假设下表:

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
于 2008-12-27T19:16:13.290 回答
1

没有比加入更清洁的方法了。如果允许字段为空,请使用外连接

SELECT c.*, s.name AS state_name
  FROM customer c
  LEFT OUTER JOIN state s ON s.id = c.state
 WHERE c.id = 10
于 2008-12-27T09:34:40.670 回答
1

根据您提供的模式描述,您将不得不在单个查询中使用 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 将允许您获取人员的详细信息,即使某些 ID 为空白或为空。

另一种方法是重新设计查找表。一个城市永远在一个州,一个州在一个国家。因此,您的city表将包含 :Id, Namestate_id. 您的state表将是 :Id, Namecountry_id。并且country表将保持不变:IdName

person表现在将只有 1 个 id :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;

注意最后两个 OUTER JOINS 的区别

于 2008-12-29T19:15:31.760 回答
0

如果涉及的表是引用表(即它们保存在会话的生命周期内不会更改的查找数据),则根据应用程序的性质,您可以在应用程序启动期间预加载引用数据. 然后,您的查询不需要进行连接,而是返回 id 值,并且在您的应用程序中,当您需要显示数据时,您可以对 id 进行解码。

于 2008-12-27T07:43:23.453 回答
0

最简单的解决方案是使用名称作为 、 和 中的citystatecountry。然后您的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 键。

于 2008-12-27T08:57:11.307 回答
0

标准 SQL 的一个缺点是返回数据需要采用表格格式。但是,一些数据库供应商添加了一些功能,可以选择非表格格式的数据。我不知道 MySQL 是否知道这些特性。

于 2008-12-27T10:30:53.177 回答
0

所有很好的答案,但提问者指定他们不想使用连接。正如一位受访者所展示的那样,假设您的CitiesStatesCountries表有一个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;
于 2008-12-29T19:26:32.347 回答
0

创建一个为您连接 Person、City、State 和 Country 的视图。然后只需在所有其他连接中引用视图。

就像是:

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
于 2008-12-29T20:14:59.347 回答
-1

连接就是答案。通过练习,它们对您来说将变得更具可读性。
在某些特殊情况下,创建函数会对您有所帮助,例如您可以执行以下操作(在 Oracle 中,我不知道任何 mysql):
您可以创建一个函数来返回给定城市州和国家/地区代码的格式化地址,那么您的查询变为

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

formated_address城市州和国家/地区表上进行个人查找并在解码值之间放置分隔符,或者如果它们都是空的,则返回“无地址”等

于 2008-12-27T07:39:38.983 回答