Вопрос

В MySQL 5.0.75-0ubuntu10.2 У меня есть фиксированный макет таблицы:

Стол parent с таблицей удостоверения личности parent2 с таблицей удостоверения личности children1 с родительским идентификатором

CREATE TABLE  `Parent` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(200) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB
CREATE TABLE  `Parent2` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(200) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB
CREATE TABLE  `Children1` (
  `id` int(11) NOT NULL auto_increment,
  `parentId` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `parent` (`parentId`)
) ENGINE=InnoDB

У детей есть родительский элемент в одной из таблиц Parent или Parent2.Когда мне нужно получить детей, я использую такой запрос:

select * from Children1 c 
inner join (
select id as parentId from Parent
union 
select id as parentId from Parent2
) p on p.parentId = c.parentId

Объяснение этот запрос дает:

+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
| id | select_type  | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra                                               |
+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
|  1 | PRIMARY      | NULL       | NULL  | NULL          | NULL    | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables | 
|  2 | DERIVED      | Parent     | index | NULL          | PRIMARY | 4       | NULL |    1 | Using index                                         | 
|  3 | UNION        | Parent2    | index | NULL          | PRIMARY | 4       | NULL |    1 | Using index                                         | 
| NULL | UNION RESULT | <union2,3> | ALL   | NULL          | NULL    | NULL    | NULL | NULL |                                                     | 
+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
4 rows in set (0.00 sec)

что разумно, учитывая планировку.

Теперь проблема:Предыдущий запрос несколько бесполезен, поскольку он не возвращает столбцы из родительских элементов.В тот момент, когда я добавляю больше столбцов во внутренний запрос, индекс больше не будет использоваться:

mysql> explain select * from Children1 c  inner join ( select id as parentId,name from Parent union  select id as parentId,name from Parent2 ) p on p.parentId = c.parentId;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------------------------------------------+
|  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables | 
|  2 | DERIVED      | Parent     | ALL  | NULL          | NULL | NULL    | NULL |    1 |                                                     | 
|  3 | UNION        | Parent2    | ALL  | NULL          | NULL | NULL    | NULL |    1 |                                                     | 
| NULL | UNION RESULT | <union2,3> | ALL  | NULL          | NULL | NULL    | NULL | NULL |                                                     | 
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------------------------------------------+
4 rows in set (0.00 sec)

Может ли кто-нибудь объяснить, почему индексы (PRIMARY) больше не используются?Есть ли обходной путь этой проблемы, если это возможно, без необходимости изменения макета БД?

Спасибо!

Это было полезно?

Решение

Я думаю, что оптимизатор отваливается, как только начинаешь выдергивать несколько столбцов в производном запросе из-за возможности, что ему придется конвертировать типы данных при объединении (не в этом случае, а вообще).Это также может быть связано с тем, что ваш запрос по сути хочет быть коррелированным производным подзапросом, что невозможно (из-за dev.mysql.com):

Подзапросы в предложении FROM не могут быть коррелированными подзапросами, если они не используются в предложении ON операции JOIN.

То, что вы пытаетесь сделать (но неверно):

select * from Children1 c 
inner join (
select id as parentId from Parent where Parent.id = c.parentId
union 
select id as parentId from Parent2 where Parent.id = c.parentId
) p 

Result: "Unknown column 'c.parentId' in 'where clause'.

Есть ли причина, по которой вы не предпочитаете два левых соединения и IFNULL:

select *, IFNULL(p1.name, p2.name) AS name from Children1 c
left join Parent p1 ON p1.id = c.parentId
left join Parent2 p2 ON p2.id = c.parentId

Единственная разница между запросами заключается в том, что в вашем вы получите две строки, если в каждой таблице есть родительский элемент.Если это то, что вам нужно/нужно, то это также будет работать хорошо, и соединения будут быстрыми и всегда будут использовать индексы:

(select * from Children1 c join Parent p1 ON p1.id = c.parentId)
union
(select * from Children1 c join Parent2 p2 ON p2.id = c.parentId)

Другие советы

Моя первая мысль — вставить «значительное» количество записей в таблицы и использовать ANALYZE TABLE для обновления статистики.Таблицу с 4 записями всегда будет быстрее читать при полном сканировании, чем при использовании индекса!Далее можно попробовать USE INDEX принудительно использовать индекс и посмотреть, как изменится план.

Я также рекомендую прочитать эту документацию и посмотреть, какие биты актуальны.MYSQL::Оптимизация запросов с помощью EXPLAIN

Эта статья также может быть полезна7 способов убедить MySQL использовать правильный индекс

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top