帮助:在MySQL中优化此查询

时间:2010-07-20 05:58:43

标签: mysql optimization mongodb query-optimization

这是我的表,AUTO_INCREMENT显示每个表的大小:

tbl_clientes

CREATE TABLE `tbl_clientes` (
  `int_clientes_id_pk` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `str_clientes_documento` varchar(255) DEFAULT NULL,
  `str_clientes_nome_original` char(255) DEFAULT NULL,
  PRIMARY KEY (`int_clientes_id_pk`),
  UNIQUE KEY `str_clientes_documento` (`str_clientes_documento`),
  KEY `str_clientes_nome_original` (`str_clientes_nome_original`),
  KEY `nome_original_cliente_id` (`str_clientes_nome_original`,`int_clientes_id_pk`),
  KEY `cliente_id_nome_original` (`int_clientes_id_pk`,`str_clientes_nome_original`)
) ENGINE=MyISAM AUTO_INCREMENT=2815520 DEFAULT CHARSET=utf8

tbl_clienteEnderecos

CREATE TABLE `tbl_clienteEnderecos` (
  `int_clienteEnderecos_id_pk` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `int_clienteEnderecos_cliente_id_fk` bigint(20) unsigned NOT NULL,
  `str_clienteEnderecos_endereco` varchar(255) NOT NULL,
  `str_clienteEnderecos_cep` varchar(255) DEFAULT NULL,
  `str_clienteEnderecos_numero` varchar(255) DEFAULT NULL,
  `str_clienteEnderecos_complemento` varchar(255) DEFAULT NULL,
  `str_clienteEnderecos_bairro` varchar(255) DEFAULT NULL,
  `str_clienteEnderecos_cidade` varchar(255) DEFAULT NULL,
  `str_clienteEnderecos_uf` varchar(2) DEFAULT NULL,
  `int_clienteEnderecos_correspondencia` tinyint(1) NOT NULL DEFAULT '0',
  `int_clienteEnderecos_tipo` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`int_clienteEnderecos_id_pk`),
  KEY `int_clienteEnderecos_cliente_id_fk` (`int_clienteEnderecos_cliente_id_fk`),
  KEY `str_clienteEnderecos_cidade` (`str_clienteEnderecos_cidade`),
  KEY `str_clienteEnderecos_uf` (`str_clienteEnderecos_uf`),
  KEY `uf_cidade` (`str_clienteEnderecos_uf`,`str_clienteEnderecos_cidade`)
) ENGINE=MyISAM AUTO_INCREMENT=1542038 DEFAULT CHARSET=utf8

然后我运行此查询进行搜索,它会很快,正在使用索引:

EXPLAIN
SELECT * FROM tbl_clientes LEFT JOIN tbl_clienteEnderecos ON int_clienteEnderecos_cliente_id_fk = int_clientes_id_pk
GROUP BY str_clientes_nome_original, int_clientes_id_pk
ORDER BY str_clientes_nome_original, int_clientes_id_pk
LIMIT 0,20

EXPAIN的结果是:

| id | select_type | table                | type  | possible_keys                      | key                                | key_len | ref                                               | rows | Extra |
+----+-------------+----------------------+-------+------------------------------------+------------------------------------+---------+---------------------------------------------------+------+-------+
|  1 | SIMPLE      | tbl_clientes         | index | NULL                               | nome_original_cliente_id           | 774     | NULL                                              |   20 |       |
|  1 | SIMPLE      | tbl_clienteEnderecos | ref   | int_clienteEnderecos_cliente_id_fk | int_clienteEnderecos_cliente_id_fk | 8       | mydb.tbl_clientes.int_clientes_id_pk |    1 |       |
+----+-------------+----------------------+-------+------------------------------------+------------------------------------+---------+---------------------------------------------------+------+-------+

好的,但我需要按 tbl_clienteEnderecos.str_clienteEnderecos_uf 进行过滤。它打破了所有索引,使用临时表和filesort(没有索引)。这是查询:

EXPLAIN
SELECT * FROM tbl_clientes LEFT JOIN tbl_clienteEnderecos ON int_clienteEnderecos_cliente_id_fk = int_clientes_id_pk
WHERE str_clienteEnderecos_uf = "SP"
GROUP BY str_clientes_nome_original, int_clientes_id_pk
ORDER BY str_clientes_nome_original, int_clientes_id_pk
LIMIT 0,20

看,这是EXPLAIN的输出:

| id | select_type | table                | type   | possible_keys                                                        | key       | key_len | ref                                                                       | rows   | Extra                                        |
+----+-------------+----------------------+--------+----------------------------------------------------------------------+-----------+---------+---------------------------------------------------------------------------+--------+----------------------------------------------+
|  1 | SIMPLE      | tbl_clienteEnderecos | ref    | int_clienteEnderecos_cliente_id_fk,str_clienteEnderecos_uf,uf_cidade | uf_cidade | 9       | const                                                                     | 670654 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | tbl_clientes         | eq_ref | PRIMARY,cliente_id_nome_original                                     | PRIMARY   | 8       | mydb.tbl_clienteEnderecos.int_clienteEnderecos_cliente_id_fk |      1 |                                              |
+----+-------------+----------------------+--------+----------------------------------------------------------------------+-----------+---------+---------------------------------------------------------------------------+--------+----------------------------------------------+

使用此 使用where;使用临时;使用filesort 它不会很快。我已经尝试了很多东西,如何优化这个查询?

是时候切换到NoSQL / MongoDB吗?

2 个答案:

答案 0 :(得分:1)

MySQL通常不会使用索引,如果它无法帮助缩小结果范围。似乎“SP”出现在大约670654行中。由于这大约是总行数的1/3,因此以磁盘顺序读取它会更有效。

您可以尝试使用tbl_clienteEnderecos的索引:

KEY `test` (`str_clienteEnderecos_uf `, `int_clienteEnderecos_cliente_id_fk`)

这可能足以让它使用索引。

这两列之间有什么区别?他们看起来应该是一样的。

int_clienteEnderecos_id_pk
int_clienteEnderecos_cliente_id_fk

修改

我理解列的名称含义。如果两个值应该相同,我只是好奇。如果是,它将简化一些事情并将它们连接到表的主键上。我不确定所涉及的表格的具体含义,因此我不知道它们之间是否存在1-1或1-0的关系或者是一对多的关系。

我建议您只尝试检索所需表格的主键。例如,而不是select * try:

EXPLAIN 
SELECT int_clienteEnerecos_id_pk, int_clientes_id_pk 
FROM tbl_clientes 
LEFT JOIN tbl_clienteEnderecos ON int_clienteEnderecos_cliente_id_fk = int_clientes_id_pk
WHERE str_clienteEnderecos_uf = "SP"
GROUP BY str_clientes_nome_original, int_clientes_id_pk
ORDER BY str_clientes_nome_original, int_clientes_id_pk
LIMIT 0,20

如果按照我希望的方式运行,您可以在Extra栏中看到“from index”。如果您需要返回其他字段,您可以再次往返获取它们,或将它们添加到索引中。或者使用嵌套查询根据上面查询的结果获取它们。

另外,你为什么要按照同样的方式进行分组和排序?你期待外键的多个匹配吗?

答案 1 :(得分:0)

我建议尝试以下内容;子查询可能比此上下文中的连接更好地使用键。但要小心;我无法发誓一堆K& R表示查询与原始查询相同。

SELECT *,
       (SELECT *
            FROM tbl_clienteEnderecos
            WHERE int_clienteEnderecos_cliente_id_fk = int_clientes_id_pk AND
                  str_clienteEnderecos_uf = "SP") AS T2
    FROM tbl_clientes
    GROUP BY str_clientes_nome_original, int_clientes_id_pk
    HAVING T2.int_clienteEnderecos_id_pk IS NOT NULL
    ORDER BY str_clientes_nome_original, int_clientes_id_pk
    LIMIT 0, 20