2009-11-12 6 views
4

Compte tenu de la structure de tableau suivant:Quelle requête MySQL JOIN est la plus efficace?

CREATE TABLE user (
    uid INT(11) auto_increment, 
    name VARCHAR(200), 
    PRIMARY KEY(uid) 
); 
CREATE TABLE user_profile(
    uid INT(11), 
    address VARCHAR(200), 
    PRIMARY KEY(uid), 
    INDEX(address) 
); 

qui se rejoignent requête est plus efficace: # 1,

SELECT u.name FROM user u INNER JOIN user_profile p ON u.uid = p.uid WHERE p.address = 'some constant' 

ou # 2:

SELECT u.name FROM user u INNER JOIN (SELECT uid FROM user_profile WHERE p.address = 'some constant') p ON u.uid = p.uid 

Quelle est la différence d'efficacité ?

+0

Je serais curieux de connaître la différence entre 2 et cette requête, ou, s'il y aurait une différence: Choisissez le nom dans utilisateur où uid IN (SELECT uid DE user_profile WHERE adresse = 'une constante'); – Drew

+2

EXPLIQUEZ les deux? Je suppose que le premier car je n'ai jamais vu une grande performance des sous-sélections dans MySQL. –

Répondre

7

La première syntaxe est généralement plus efficace.

MySQL buffers les requêtes dérivées de sorte que l'utilisation de la requête dérivée prive le user_profile de possibilité d'être une table pilotée dans la jointure.

Même si le user_profile est en tête, les résultats des sous-requêtes doivent être mises en mémoire tampon premier qui implique un impact de la mémoire et de performance.

A LIMIT appliqué aux requêtes fera la première requête beaucoup plus rapide qui est pas vrai pour le second.

Voici les plans d'échantillonnage. Il y a un index sur (val, nid) dans le tableau t_source:

première requête:

EXPLAIN 
SELECT * 
FROM t_source s1 
JOIN t_source s2 
ON  s2.nid = s1.id 
WHERE s2.val = 1 

1, 'SIMPLE', 's1', 'ALL', 'PRIMARY', '', '', '', 1000000, '' 
1, 'SIMPLE', 's2', 'ref', 'ix_source_val,ix_source_val_nid,ix_source_vald_nid', 'ix_source_val_nid', '8', 'const,test.s1.id', 1, 'Using where' 

seconde requête:

EXPLAIN 
SELECT * 
FROM t_source s1 
JOIN (
     SELECT nid 
     FROM t_source s2 
     WHERE val = 1 
     ) q 
ON  q.nid = s1.id 

1, 'PRIMARY', '<derived2>', 'ALL', '', '', '', '', 100000, '' 
1, 'PRIMARY', 's1', 'ref', 'PRIMARY', 'PRIMARY', '4', 'q.nid', 10000, 'Using where' 
2, 'DERIVED', 's2', 'ref', 'ix_source_val,ix_source_val_nid,ix_source_vald_nid', 'ix_source_vald_nid', '4', '', 91324, 'Using index' 

Comme vous pouvez le voir, seule une partie de l'indice est utilisé dans la deuxième cas, et q est forcé d'être en tête.

Mise à jour:

requêtes dérivées (ce qui est ce que cela concerne la question) ne doivent pas être confondus avec les sous-requêtes.

Alors que MySQL n'est pas en mesure d'optimiser les requêtes dérivées (celles qui sont utilisées dans la clause FROM), les (ceux des sous-requêtes utilisées avec IN ou EXISTS) sont beaucoup mieux traités.

Voir ces articles dans mon blog pour plus de détails:

+0

+1 pour une réponse détaillée, et merci pour les mises en garde sur les échecs ridicules de l'optimiseur MySQL; J'ai déjà eu des problèmes avec ça, mais rien de si médiocre. –

0

Je ne sais pas comment le moteur de recherche de MySQL serait gérer cela, mais mon hypothèse serait la première requête fonctionnerait mieux et serait plus efficace.

La première requête est plus standard et plus facile à lire des deux donc plus préférable.

0

La réponse dépend généralement des statistiques collectées par la base de données. La première forme semble être plus facile pour l'optimiseur.

Pour autant que je me rappelle, MySQL ne fonctionne pas bien avec IN ... les requêtes et les sous-requêtes

+0

La première forme est meilleure en effet, mais 'IN' est un cas spécial dans' MySQL' et il est très bien optimisé. Vous pouvez lire cet article: http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/ – Quassnoi

1

En regardant les requêtes pour expliquer ces sélections, nous obtenons ceci: (en-têtes de ligne sont id, select_type, table , type, possible_keys, clés, key_len, ref, des lignes supplémentaires)

1 SIMPLE u system PRIMARY NULL NULL NULL 1 
1 SIMPLE p const PRIMARY,address PRIMARY 4 const 1 

et Explain pour la deuxième ...

1 PRIMARY u system PRIMARY NULL NULL NULL 1 
1 PRIMARY <derived2> system NULL NULL NULL NULL 1  
2 DERIVED p ref address address 201  1 Using where 

Ainsi, la première requête est plus simple et plus simple est généralement plus efficace. Toutefois, à partir de vos CREATE, il serait beaucoup plus efficace d'ajouter le champ d'adresse à la table utilisateur. Puisque le profil est 1 à 1 avec la table utilisateur (sur uid), il est possible de combiner les tables tout en gardant le schéma normalisé.

Ensuite, votre requête serait

SELECT u.name FROM user u WHERE u.address = 'some constant' 

et l'expliquer montre

1 SIMPLE u ref address address 201 const 1 Using where, using filesort 

Bizarrement, le schéma simplifié utilise filesorting, ce qui est mauvais si vous avez beaucoup de lignes.

En savoir plus sur expliquer: http://dev.mysql.com/doc/refman/5.0/en/explain.html

+0

Je n'ai jamais vu le filesort dans une telle requête dans 'MySQL', mais dans d'autres systèmes ceci est utilisé pour accélérer la recherche d'index. Il prend les pointeurs de ligne de l'index et les trie de sorte que les recherches de ligne soient séquentielles. Les avantages de l'accès séquentiel à la table surpasser le coût pour trier les résultats de l'indice. – Quassnoi

+0

J'ai été assez surpris qu'il ait aussi fait un fichier, mais il peut s'agir d'un porc de performance si vous avez une grande table. Si vous avez environ 1000 résultats, il faudra un certain temps pour l'écrire dans un fichier, trier les lignes dans le fichier, puis continuer. Intéressant que c'est une * optimisation * cependant. Je ne suis pas très familier avec les internes des DB. – davethegr8

+1

Dans ce cas, 'filesort' n'écrit pas dans un fichier. Il peut en effet écrire dans un fichier (ce qui serait reflété dans le plan comme «using temporary»), mais ce n'est pas le cas dans ce cas. 'Filesort' est mal nommé, il devrait être juste un' sort'. – Quassnoi

Questions connexes