无法使联接正常工作

时间:2015-07-28 10:40:52

标签: mysql join multiple-tables

我有以下数据:

customer
+---------------------------------------------------------------------------------------------+
| id    | email             | firstname     | lastname      | no_pub    | customer_address    |
|---------------------------------------------------------------------------------------------|
| 1     | martin@domain.com | Martin        | Scorcese      | 0         | 4                   |
| 2     | robert@domain.com | Robert        | De Niro       | 0         | 7                   |
| 3     | bruce@domain;com  | Bruce         | Willis        | 0         | 10                  |
+---------------------------------------------------------------------------------------------+

address
+------------------------------------------+
| id    | city             | zipcode       |
|------------------------------------------|
| 4     | Paris            | 75001         |
| 7     | Marseille        | 13000         |
| 10    | Bordeaux         | 33000         |
+------------------------------------------+

sf_geo_cities
+------------------------------------------+
| id    | region_id    | zipcode           |
|------------------------------------------|
| 1     | 1            | 75001             |
| 2     | 2            | 13000             |
| 2     | 3            | 33000             |
+------------------------------------------+

sf_geo_regions
+------------------------------------------+
| id    | name             | zipcode       |
|------------------------------------------|
| 1     | Ile-de-France    | 75001         |
| 2     | Cote d'Azur      | 13000         |
| 2     | Gironde          | 33000         |
+------------------------------------------+

这是一个示例,这些表中显然有更多数据(300k +客户,400k +地址) 最后,我想获得以下数据:

+---------------------------------------------------------------------------------------------+
| id    | email             | firstname     | lastname      | city          | region          |
|---------------------------------------------------------------------------------------------|
| 1     | martin@domain.com | Martin        | Scorcese      | Paris         | Ile-de-France   |
| 2     | robert@domain.com | Robert        | De Niro       | Marseille     | Cote d'Azur     |
| 3     | bruce@domain;com  | Bruce         | Willis        | NULL          | NULL            |
+---------------------------------------------------------------------------------------------+

我尝试了以下SQL查询:

SELECT c.id, c.email, c.firstname, c.lastname, gc.name, gr.name
FROM customer c
LEFT JOIN address ad ON ad.id = c.customer_address
JOIN sf_geo_cities gc ON gc.zipcode = ad.zipcode
JOIN sf_geo_regions gr ON gr.id = gc.region_id
WHERE no_pub = 0

但这需要很长时间,我必须关闭mysql服务。连接乘法行可能存在问题。

是否有一个简单的查询来获取我期望的数据?

更新#1: 以下是评论中的EXPLAIN:

id  select_type     table   type    possible_keys                                           key         key_len     ref                         rows    Extra   
1   SIMPLE          c       ref     PRIMARY,UNIQ_81398E097D3656A4,UNIQ_81398E09E7927C7...   no_pub      1           const                       136220  Using where
1   SIMPLE          a       eq_ref  PRIMARY                                                 PRIMARY     8           evotest.c.account           1       Using index
1   SIMPLE          ad      eq_ref  PRIMARY                                                 PRIMARY     8           evotest.c.customer_address  1       NULL
1   SIMPLE          gc      ALL     zipcode                                                 NULL        NULL        NULL                        38194   Range checked for each record (index map: 0x8)
1   SIMPLE          gr      eq_ref  PRIMARY                                                 PRIMARY     4           evotest.gc.region_id        1       NULL

更新#2:数据库示例 这是我的数据库,包含最少的样本数据。

--
-- Structure de la table `account`
--

CREATE TABLE IF NOT EXISTS `account` (
`id` bigint(20) unsigned NOT NULL,
  `identifier` varchar(255) COLLATE utf8_bin NOT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=315688 ;

--
-- Structure de la table `customer`
--

CREATE TABLE IF NOT EXISTS `customer` (
`id` bigint(20) unsigned NOT NULL,
  `account` bigint(20) unsigned NOT NULL,
  `customer_address` bigint(20) unsigned DEFAULT NULL,
  `email` varchar(255) COLLATE utf8_bin NOT NULL,
  `lastname` varchar(255) COLLATE utf8_bin NOT NULL,
  `firstname` varchar(255) COLLATE utf8_bin NOT NULL,
  `no_pub` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=315224 ;

--
-- Structure de la table `address`
--

CREATE TABLE IF NOT EXISTS `address` (
`id` bigint(20) unsigned NOT NULL,
  `city` varchar(64) COLLATE utf8_bin DEFAULT NULL,
  `street` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `complement` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `zipcode` varchar(16) COLLATE utf8_bin DEFAULT NULL,
  `country_id` int(11) DEFAULT NULL,
  `cedex` tinyint(1) NOT NULL DEFAULT '0',
  `abroad` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=442743 ;

--
-- Structure de la table `sf_geo_cities`
--

CREATE TABLE IF NOT EXISTS `sf_geo_cities` (
`id` int(11) NOT NULL,
  `region_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `slug` varchar(255) NOT NULL,
  `zipcode` varchar(5) NOT NULL,
  `insee_code` int(11) NOT NULL,
  `latitude` float NOT NULL,
  `longitude` float NOT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=38106 ;

CREATE TABLE IF NOT EXISTS `sf_geo_regions` (
`id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=25 ;

CREATE TABLE IF NOT EXISTS `sf_geo_countries` (
`id` int(11) NOT NULL,
  `code` int(11) NOT NULL,
  `alpha2` varchar(2) NOT NULL,
  `alpha3` varchar(3) NOT NULL,
  `name_en` varchar(45) NOT NULL,
  `name_fr` varchar(45) NOT NULL,
  `is_default` tinyint(1) NOT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=242 ;

CREATE TABLE IF NOT EXISTS `sf_user_data` (
`id` int(11) NOT NULL,
  `user_id` bigint(20) unsigned NOT NULL,
  `main_activity_type_id` int(11) DEFAULT NULL,
  `main_activity_id` int(11) DEFAULT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=19001 ;


CREATE TABLE IF NOT EXISTS `sf_activity_types` (
`id` int(11) NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `identifier` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Ne doit pas être modifié, il s agit de la clé dans le tableau de constantes $constants stockant les taux dans l entité Calculator'
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;

CREATE TABLE IF NOT EXISTS `sf_activities` (
`id` int(11) NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `asks_for_custom` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=27 ;

--
-- Index pour les tables exportées
--

ALTER TABLE `account`
 ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `identifier_UNIQUE` (`identifier`);

ALTER TABLE `customer`
 ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `UNIQ_81398E097D3656A4` (`account`), ADD UNIQUE KEY `UNIQ_81398E09E7927C74` (`email`), ADD UNIQUE KEY `UNIQ_81398E091193CB3F` (`customer_address`), ADD KEY `no_pub` (`no_pub`);

ALTER TABLE `address`
 ADD PRIMARY KEY (`id`), ADD KEY `IDX_D4E6F81F92F3E70` (`country_id`), ADD KEY `zipcode` (`zipcode`); 

ALTER TABLE `sf_geo_cities`
 ADD PRIMARY KEY (`id`), ADD KEY `IDX_B56556A198260155` (`region_id`), ADD KEY `zipcode` (`zipcode`);

ALTER TABLE `sf_geo_regions`
 ADD PRIMARY KEY (`id`);

ALTER TABLE `sf_geo_countries`
 ADD PRIMARY KEY (`id`), ADD KEY `IDX_F86325E277153098` (`code`), ADD KEY `IDX_F86325E2B762D672` (`alpha2`), ADD KEY `IDX_F86325E2C065E6E4` (`alpha3`);

ALTER TABLE `sf_user_data`
 ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `UNIQ_E904BFD1A76ED395` (`user_id`), ADD KEY `IDX_E904BFD12E864BE8` (`main_activity_type_id`), ADD KEY `IDX_E904BFD15543A800` (`main_activity_id`);

ALTER TABLE `sf_activity_types`
 ADD PRIMARY KEY (`id`);

ALTER TABLE `sf_activities`
 ADD PRIMARY KEY (`id`); 

--
-- Contraintes pour les tables exportées
--

ALTER TABLE `customer`
ADD CONSTRAINT `FK_81398E091193CB3F` FOREIGN KEY (`customer_address`) REFERENCES `address` (`id`),
ADD CONSTRAINT `FK_81398E097D3656A4` FOREIGN KEY (`account`) REFERENCES `account` (`id`);

ALTER TABLE `address`
ADD CONSTRAINT `FK_D4E6F81F92F3E70` FOREIGN KEY (`country_id`) REFERENCES `sf_geo_countries` (`id`);

ALTER TABLE `sf_geo_cities`
ADD CONSTRAINT `FK_B56556A198260155` FOREIGN KEY (`region_id`) REFERENCES `sf_geo_regions` (`id`);

ALTER TABLE `sf_user_data`
ADD CONSTRAINT `FK_E904BFD12E864BE8` FOREIGN KEY (`main_activity_type_id`) REFERENCES `sf_activity_types` (`id`),
ADD CONSTRAINT `FK_E904BFD15543A800` FOREIGN KEY (`main_activity_id`) REFERENCES `sf_activities` (`id`),
ADD CONSTRAINT `FK_E904BFD1A76ED395` FOREIGN KEY (`user_id`) REFERENCES `account` (`id`);


INSERT INTO `account` (`id`, `identifier`) VALUES ('1', 'martin@domain.com'), ('2', 'robert@domain.com'), ('3', 'bruce@domain.com');

INSERT INTO `sf_geo_countries` (`id`, `code`, `alpha2`, `alpha3`, `name_en`, `name_fr`, `is_default`) VALUES ('1', '1', 'FR', 'FRA', 'France', 'France', '1');

INSERT INTO `address` (`id`, `city`, `street`, `complement`, `zipcode`, `country_id`, `cedex`, `abroad`) VALUES ('1', 'Paris', '1 rue de Paris', NULL, '75001', '1', '0', '0'), ('2', 'Marseille', '1 rue de Marseille', NULL, '13000', '1', '0', '0');

INSERT INTO `customer` (`id`, `account`, `customer_address`, `email`, `lastname`, `firstname`, `no_pub`) VALUES ('1', '1', '1', 'martin@domain.com', 'Scorcese', 'Martin', '0'), ('2', '2', '2', 'robert@domain.com', 'De Niro', 'Robert', '0'), ('3', '3', NULL, 'bruce@domain.com', 'Willis', 'Bruce', '0');

INSERT INTO `sf_activities` (`id`, `name`, `asks_for_custom`) VALUES ('1', 'Activity #1', '0'), ('2', 'Activity #2', '0');

INSERT INTO `sf_activity_types` (`id`, `name`, `identifier`) VALUES ('1', 'Activity Type #1', 'activity-type-1'), ('2', 'Activity Type #2', 'activity-type-2');

INSERT INTO `sf_geo_regions` (`id`, `name`) VALUES ('1', 'Ile-de-France'), ('2', 'Cote d''Azur');

INSERT INTO `sf_geo_cities` (`id`, `region_id`, `name`, `slug`, `zipcode`, `insee_code`, `latitude`, `longitude`) VALUES ('1', '1', 'Paris', 'paris', '75001', '1', '0', '0'), ('2', '2', 'Marseille', 'marseille', '13000', '2', '0', '0');

INSERT INTO `sf_user_data` (`id`, `user_id`, `main_activity_type_id`, `main_activity_id`) VALUES ('1', '1', '1', '1'), ('2', '3', '2', '2');

使用这么少量的数据,我可以运行以下查询,返回我想要的一切。但是这个查询在我的真实数据库中运行了很长时间,拥有300k +客户和400k +地址

SELECT c.id, c.email, c.firstname, c.lastname, acttypes.name AS activity_type, act.name AS activity, gc.name AS city, gr.name AS region
FROM customer c
JOIN account a ON a.id = c.account
LEFT JOIN sf_user_data ud ON ud.user_id = a.id
LEFT JOIN sf_activity_types acttypes ON acttypes.id = ud.main_activity_type_id
LEFT JOIN sf_activities act ON act.id = ud.main_activity_id
LEFT JOIN address ad ON ad.id = c.customer_address
LEFT JOIN sf_geo_cities gc ON gc.zipcode = ad.zipcode
LEFT JOIN sf_geo_regions gr ON gr.id = gc.region_id
WHERE no_pub = 0

更新#3:跟踪加入问题

此查询运行速度很快:

SELECT 
    c.id 
    ,c.email 
    ,c.firstname 
    ,c.lastname 
    ,acttypes.name AS activity_type 
    ,act.name AS activity 
    ,ad.zipcode AS address_zipcode
--    ,gc.name AS city 
--    ,gr.name AS region    
FROM  
    customer c 
JOIN account a ON a.id = c.account
    LEFT JOIN sf_user_data ud ON ud.user_id = a.id
    LEFT JOIN sf_activity_types acttypes ON acttypes.id = ud.main_activity_type_id
    LEFT JOIN sf_activities act ON act.id = ud.main_activity_id
    LEFT JOIN address ad ON ad.id = c.customer_address
--    LEFT JOIN sf_geo_cities gc ON gc.zipcode = ad.zipcode
--    LEFT JOIN sf_geo_regions gr ON gr.id = gc.region_id
WHERE 
    no_pub = 0 

此查询也可以快速运行:

SELECT 
    c.id 
    ,c.email 
    ,c.firstname 
    ,c.lastname 
--    ,acttypes.name AS activity_type 
--    ,act.name AS activity 
    ,ad.zipcode AS address_zipcode
    ,gc.name AS city 
--    ,gr.name AS region    
FROM  
    customer c 
JOIN account a ON a.id = c.account
--    LEFT JOIN sf_user_data ud ON ud.user_id = a.id
--    LEFT JOIN sf_activity_types acttypes ON acttypes.id = ud.main_activity_type_id
--    LEFT JOIN sf_activities act ON act.id = ud.main_activity_id
    LEFT JOIN address ad ON ad.id = c.customer_address
    LEFT JOIN sf_geo_cities gc ON gc.zipcode = ad.zipcode
--    LEFT JOIN sf_geo_regions gr ON gr.id = gc.region_id
WHERE 
    no_pub = 0 

此查询永远运行:

SELECT 
    c.id 
    ,c.email 
    ,c.firstname 
    ,c.lastname 
    ,acttypes.name AS activity_type 
    ,act.name AS activity 
    ,ad.zipcode AS address_zipcode
    ,gc.name AS city 
--    ,gr.name AS region    
FROM  
    customer c 
JOIN account a ON a.id = c.account
    LEFT JOIN sf_user_data ud ON ud.user_id = a.id
    LEFT JOIN sf_activity_types acttypes ON acttypes.id = ud.main_activity_type_id
    LEFT JOIN sf_activities act ON act.id = ud.main_activity_id
    LEFT JOIN address ad ON ad.id = c.customer_address
    LEFT JOIN sf_geo_cities gc ON gc.zipcode = ad.zipcode
--    LEFT JOIN sf_geo_regions gr ON gr.id = gc.region_id
WHERE 
    no_pub = 0

加入活动是可以的,加入地址是可以的,但尝试使用所有这些联接并不起作用。

1 个答案:

答案 0 :(得分:1)

正如我的评论中提到的,非常重要的一点是返回了多少条记录。一个语句中的记录太多可能导致问题,如果您对结果进行分页,一次选择n条记录,则可能会更好。

要对查询进行疑难解答,请通过联接构建它以尝试确定性能何时落入悬崖。从:

开始
go build

然后一次取消注释一个连接,直到您的查询执行速度非常慢。分析该查询并查看是否可以使用索引进行改进。然后继续取消注释连接,直到您有完整的查询。