Domanda

In MySQL 5.0.75-0ubuntu10.2 ho un layout di tabella fisso come quello:

Tabella parent con un ID Tabella parent2 con un ID Tabella children1 con parentId

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

Un figlio ha un genitore in una delle tabelle Parent o Parent2 . Quando ho bisogno di avere un figlio uso una query del genere:

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

Spiegando questa query produce:

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

che è ragionevole dato il layout.

Ora il problema: la query precedente è alquanto inutile, poiché non restituisce colonne dagli elementi padre. Nel momento in cui aggiungo più colonne alla query interna, non verrà più utilizzato alcun indice:

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)

Qualcuno può spiegare perché gli indici (PRIMARY) non vengono più utilizzati? Esiste una soluzione alternativa per questo problema, se possibile, senza dover modificare il layout del DB?

Grazie!

È stato utile?

Soluzione

Penso che l'ottimizzatore cada una volta che inizi a estrarre più colonne nella query derivata a causa della possibilità che debba convertire i tipi di dati sull'unione (non in questo caso, ma in generale). Potrebbe anche essere dovuto al fatto che la tua query vuole essenzialmente essere una sottoquery derivata correlata, il che non è possibile (da dev.mysql.com ):

  

Le subquery nella clausola FROM non possono essere subquery correlate, a meno che non vengano utilizzate nella clausola ON di un'operazione JOIN.

Quello che stai cercando di fare (ma non è valido) è:

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'.

C'è un motivo per cui non preferisci due join a sinistra e 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

L'unica differenza tra le query è che nelle tue otterrai due righe se c'è un genitore in ogni tabella. Se è quello che vuoi / hai bisogno, allora funzionerà bene e i join saranno veloci e faranno sempre uso degli indici:

(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)

Altri suggerimenti

Il mio primo pensiero è quello di inserire un "significativo" numero di record nelle tabelle e utilizzare TABELLA ANALISI per aggiornare le statistiche. Una tabella con 4 record sarà sempre più veloce da leggere usando una scansione completa anziché passare attraverso l'indice! Inoltre, puoi provare USE INDEX per forzare l'utilizzo dell'indice e vedere come cambia il piano.

Consiglio anche di leggere questa documentazione e vedere quali bit sono rilevanti MYSQL :: Ottimizzazione delle query con EXPLAIN

Questo articolo può anche essere utile 7 modi per convincere MySQL per utilizzare l'indice giusto

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top