MySQL - 非常简单加入耗时太长

时间:2014-10-17 13:42:03

标签: mysql sql join indexing

这是我在stackoverflow中的第一个问题,我很高兴成为这个社区的一员,因为它多次帮助了我。

我不是SQL和MySQL的专家,但我在一个需要大表(百万行)的项目中工作。 我在加入时遇到问题而且我不明白为什么需要这么长时间。在此先感谢:)

以下是表格:

CREATE TABLE IF NOT EXISTS tabla_maestra(
id int UNIQUE,
codigo_alta char(1),
nombre varchar(100),
empresa_apellido1 varchar(150),
apellido2 varchar(50),
tipo_via varchar(20),
nombre_via varchar(100),
numero_via varchar(50),
codigo_via char(5),
codigo_postal char(5),
nombre_poblacion varchar(100),
codigo_ine char(11),
nombre_provincia varchar(50),
telefono varchar(250) UNIQUE,
actividad varchar(100),
estado char(1),
codigo_operadora char(3)
);

CREATE TABLE IF NOT EXISTS tabla_actividades_empresas(
empresa_apellido1 varchar(150),
actividad varchar(100)
);

这是我想要的查询:

UPDATE tabla_maestra tm
INNER JOIN tabla_actividades_empresas tae
ON (tm.nombre!='' AND tae.empresa_apellido1=tm.empresa_apellido1)
SET tm.actividad=tae.actividad;

这个查询花了太长时间,在执行之前我试图测试这个简单查询需要多长时间:

SELECT COUNT(*) FROM tabla_maestra tm
INNER JOIN tabla_actividades_empresas tae 
ON (tm.nombre!='' AND tae.empresa_apellido1=tm.empresa_apellido1);

它仍然需要太长时间,我不明白为什么。以下是我使用的索引:

CREATE INDEX cruce_nombre
USING HASH
ON tabla_maestra (nombre);

CREATE INDEX cruce_empresa_apellido1
USING HASH
ON tabla_maestra (empresa_apellido1);

CREATE INDEX index_actividades_empresas
USING HASH
ON tabla_actividades_empresas(empresa_apellido1);

如果我使用EXPLAIN语句,结果如下:

http://oi59.tinypic.com/2zedoy0.jpg

我很感激收到任何可以帮助我的答案。非常感谢, 达尼。

3 个答案:

答案 0 :(得分:1)

如您的查询计划所示,涉及五十万行的联接必然需要一些时间。 count(*)查询更快,因为它不需要读取tabla_maestra表本身,但它仍然需要扫描索引cruce_empresa_apellido1的所有行。

如果您将索引index_actividades_empresas设为唯一索引(假设确实合适),或者您放弃该索引并创建列{{1>表empresa_apellido1的主键。

如果即使这样也没有给你足够的性能,那么我唯一要做的就是给表tabla_actividades_empresas一个整数类型的合成主键,并更改tabla_maestra的相应列以匹配。这应该有所帮助,因为将整数与整数进行比较比将字符串与字符串进行比较要快,即使您可以通过散列过滤掉(大多数)不匹配。

答案 1 :(得分:1)

我同意其他人(参见John Bollinger)关于缺少主键的问题。它非常喜欢ID(我注意到你担心它会被重复,但PK也会顺利地对待它 - 我的意思是MySQL的自动识别)。

为什么使用tabla_actividades_empresasempresa_apellido1而不是查找要引用的tabla_maestra ID?

如果是这样,你可以为它定义外键:tabla_actividades_empresasmaestra_id

因为如果将表与非字符串类型相关联会更好。

您还可以在表之间的JOIN操作之前对表进行子查询。这是一个例子:

UPDATE (SELECT * FROM tabla_maestra WHERE nombre != '') AS tm
INNER JOIN tabla_actividades_empresas AS tae
ON tae.empresa_apellido1 = tm.empresa_apellido1
SET tm.actividad = tae.actividad;

我还没有测试过。但从那时起它似乎是一个很好的行为。

哦......每次你需要更新所有数据行吗?除非,您只能更新被遗忘的人。您可以在UPDATE之后INNER JOIN之前应用LEFT JOIN来确定需要更新的所需内容。它有意义吗?我不是任何专家,但考虑一下会很有用。

修改

您也可以测试一些子查询:

UPDATE tabla_maestra AS main, tabla_actividades_empresas AS aggr
SET main.actividad = aggr.actividad
WHERE main.empresa_apellido1 = aggr.empresa_apellido1
AND main.nombre <> ''

不要忘记尝试调整关系。

答案 2 :(得分:0)

非常感谢您的回答。

事实上,表格 tabla_maestra 是一个包含企业信息的表格,但不包含“激活资料”字段的值(企业)。此外,' id '字段仍然是空的(我将在未来使用它。很难解释原因,但必须以这种方式完成)。

我需要添加每个企业加入一个辅助表' tabla_actividades_empresas '的活动,其中包含每个企业名称的活动。我只需要做一次,不再需要。然后,我可以删除“ tabla_actividades_empresas ”表格,因为我不需要它。

加入他们的唯一方法是“ empresa_apellido1 ”字段,也就是企业名称。

我已将“ tabla_actividades_empresas.empresa_apellido1 ”字段设为唯一,但它并未提高性能。

在“ tabla_actividades_empresas ”上定义外键是没有意义的,因为“ empresa_apellido1 ”字段仅对' tabla_actividades_empresas <是唯一的/ strong>',而不是' tabla_maestra '(在此表中,企业名称可以多次出现,因为企业可以在不同的地方拥有不同的办事处)。也就是说,' tabla_actividades_empresas '不包含重复的企业,但' tabla_maestra '重复了名称企业。

顺便说一下,“调整关系”是什么意思?我已经使用explain语句尝试了你的子查询,并且它没有正确使用索引,性能更差。