Справка: оптимизировать этот запрос в MySQL - PullRequest
0 голосов
/ 20 июля 2010

Это мои таблицы, 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 .Он ломает все индексы, использует временную таблицу и сортировку файлов (без индекса).Вот запрос:

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 |                                              |
+----+-------------+----------------------+--------+----------------------------------------------------------------------+-----------+---------+---------------------------------------------------------------------------+--------+----------------------------------------------+

С этим Using where;Используя временные;Использование сортировки файлов не может быть быстрым.Я много чего перепробовал, как оптимизировать этот запрос?

Не пора ли перейти на NoSQL / MongoDB?

Ответы [ 2 ]

1 голос
/ 20 июля 2010

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.Если вам нужны возвращенные дополнительные поля, вы можете либо совершить еще одну поездку в оба конца, чтобы получить их, либо добавить их в свой индекс.Или используйте вложенный запрос, чтобы выбрать их на основе результатов запроса, приведенного выше.

Кроме того, почему вы группируете и упорядочиваете по одной и той же вещи?Ожидаете ли вы несколько совпадений внешнего ключа?

0 голосов
/ 20 июля 2010

Я бы предложил попробовать следующее;подзапрос может использовать ключ лучше, чем соединение в этом контексте.Береги себя, хотя;Я не мог поклясться на стеке 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
...