在两个表之间查询不直接相关的数据的最佳方法是什么

时间:2013-10-11 20:52:00

标签: mysql sql database-design relational-database

在两个表之间查询数据的最佳方法是什么?这两个表没有直接关联,而是通过第三个甚至第四个表,使用外键相关?

类似于1 -> 2 -> 3 -> 4

我看到了许多非常不同的建议,但是想了解在这种情况下最好的方法,我尝试维护关系数据库模型,同时避免超级复杂的SQL查询。

以下示例符合我的情况:

CREATE TABLE `user` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) DEFAULT NULL,
    `city_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_city_id` FOREIGN KEY (`city_id`) 
    REFERENCES `city` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE `city` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) DEFAULT NULL,
    `country_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_country_id` FOREIGN KEY (`country_id`) 
    REFERENCES `country` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE `country` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) DEFAULT NULL,
    `continent_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_continent_id` FOREIGN KEY (`continent_id`) 
    REFERENCES `continent` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE `continent` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

让我们说我想让所有用户来自特定的大陆

SELECT name FROM user WHERE city_id IN (
    SELECT id FROM city WHERE country_id IN (
        SELECT id FROM country WHERE continent_id = 1 ) ) );

我认为看起来不错,但如果我想让特定城市的所有用户

,该怎么办?
SELECT name FROM user WHERE city_id IN (
  SELECT id FROM city WHERE country_id IN (
    SELECT id FROM country WHERE continent_id = (
      SELECT continent_id FROM country WHERE id = (
        SELECT country_id FROM city WHERE id = 1 ) ) ) );

效率这么高吗?

也许可以通过JOIN实现相同的目标吗?

我试图避免将所有值添加到用户表,以维持该关系city->country->continent并让关系完成工作,但也许在这种情况下,它不值得这样做? ..可能效率不高,重新设计数据库会更好吗?

2 个答案:

答案 0 :(得分:1)

内部联接

-- get users to cities
from
 City c
 inner join Users u
  on c.Id = u.City_Id

-- users to continents
from
 Users u
 inner join City ci
  on u.City_id = ci.Id
 inner join Country co
  on ci.Country_Id = co.Id
 inner join Continent con
  on co.Continent_Id = con.Id

答案 1 :(得分:1)

应该是这样的,你可能想分析(解释为什么JOIN比子查询更好)EXPLAIN手册页http://dev.mysql.com/doc/refman/5.7/en/explain.html

请注意,您只能通过LEFT JOIN或RIGHT JOIN使用下面的过滤器信任INNER JOIN,您可能会得到错误的结果..

适用于continent_id = 1

的大陆内的所有用户
SELECT
 user.name 

FROM
 user

INNER JOIN 
 city
ON
 user.city_id = city.id

INNER JOIN 
 country
ON
  city.country_id = country.id
 AND
  country.continent_id = 1
;

适用于city_id = 1

的城市内的所有用户
SELECT
 user.name 

FROM
 user

INNER JOIN 
 city
ON
  user.city_id = city.id
 AND
  city.id = 1

请参阅http://sqlfiddle.com/#!2/1c40e/23