2009-08-07 2 views
5

J'ai une grande table (TokenFrequency) qui contient des millions de lignes. Le tableau de TokenFrequency qui est structuré comme suit:SQL Alternative à l'exécution d'un INNER JOIN sur une seule table

Tableau - TokenFrequency

  • id - int, clé primaire la source
  • - int, clé étrangère
  • jeton - comte carboniser
  • - int

Mon objectif est de sélectionner toutes les lignes dans lesquelles deux sources ont le même jeton. Par exemple, si ma table ressemblait à ceci:

id --- Source --- --- jeton compte
1 ------ 1 chien --------- - ----- 1
2 ------ 2 --------- cat -------- 2
3 ------ 3 ----- ---- cat -------- 2
4 ------ 4 --------- cochon -------- 5
5 ---- - 5 --------- zoo ------- 1
6 ------ 5 --------- cat -------- 1
7 ------ 5 --------- porc -------- 1

Je voudrais qu'une requête SQL me donne la source 1, la source 2, et la somme des comptes. Par exemple:

source1 --- --- source2 jeton --- compte
---- 2 ----------- 3 --------- chat -------- 4
---- 2 ----------- 5 --------- cat -------- 3
---- 3 ----------- 5 --------- cat -------- 3
---- 4 ------- ---- 5 -------- --------- porc 6

J'ai une requête qui ressemble à ceci:

SELECT F.source AS source1, S.source AS source2, F.token, 
     (F.count + S.count) AS sum 
FROM  TokenFrequency F 
INNER JOIN TokenFrequency S ON F.token = S.token 
WHERE F.source <> S.source 

Cette requête fonctionne très bien, mais les problèmes que j'ai avec ce sont que:

  1. J'ai une table de TokenFrequency qui a des millions de lignes et donc besoin d'une alternative plus rapide pour obtenir ce résultat.
  2. La requête en cours que j'ai est de donner des doublons. Par exemple sa sélection:
    source1 = 2, source2 = 3, jeton = chat, count = 4
    source1 = 3, source2 = 2, jeton = chat, count = 4
    Ce qui est pas trop d'un problème mais s'il y a un moyen d'éliminer ceux-ci et d'obtenir une augmentation de vitesse alors il serait très utile

Le principal problème que j'ai est la vitesse de la requête avec ma requête actuelle, il faut des heures pour terminer. Le INNER JOIN sur une table à lui-même est ce que je crois être le problème. Je suis sûr qu'il doit y avoir un moyen d'éliminer la jointure interne et obtenir des résultats similaires en utilisant une seule instance de la table TokenFrequency. Le deuxième problème que j'ai mentionné pourrait également favoriser une augmentation de la vitesse dans la requête.

J'ai besoin d'un moyen de restructurer cette requête pour fournir les mêmes résultats de manière plus rapide et plus efficace.

Merci.

+1

Pouvez-vous publier l'EXPLAIN de la requête (http://dev.mysql.com/doc/refman/5.0/en/explain.html). Cela aidera les gens à voir comment ils peuvent vous aider à optimiser. –

+0

vous devez donner quelques informations d'index, quelles colonnes, etc. –

+0

Voici mon EXPLAIN de la requête que j'ai initialement publiée. id: 1, select_type: SIMPLE, table: F & S, tapez: ALL, touches possibles: NULL, clé: NULL, Key_len: NULL, réf: NULL, lignes: 8, Extra: Using where; Utilisation du tampon de jointure Deux lignes sont renvoyées. La seule différence réside dans les deux noms de tables F et S. – cruzja

Répondre

2

je besoin d'un peu plus d'informations pour diagnostiquer le problème de la vitesse, mais pour supprimer les dups, ajoutez au OÙ:

AND F.source<S.source 
+0

Ah si simple. Cela a fonctionné parfaitement pour éliminer les doublons. Merci – cruzja

2

Essayez ceci:

SELECT token, GROUP_CONCAT(source), SUM(count) 
FROM TokenFrequency 
GROUP BY token; 

Cela devrait fonctionner beaucoup plus rapide et aussi éliminer les doublons. Mais les sources seront renvoyées dans une liste séparée par des virgules, vous devrez donc l'exploser dans votre application.

Vous pouvez également essayer de créer un index composé sur les colonnes token, source, count (dans cet ordre) et d'analyser avec EXPLAIN pour voir si MySQL est assez intelligent pour l'utiliser comme un covering index pour cette requête.


mise à jour: Il me semble avoir mal compris votre question. Vous ne voulez pas la somme des comptes par jeton, vous voulez la somme des comptes pour chaque paire de sources pour un jeton donné. Je crois que la jointure interne est la meilleure solution pour cela. Une directive importante pour SQL est que si vous avez besoin de calculer une expression par rapport à deux lignes différentes, vous devez faire une jointure.

Cependant, une technique d'optimisation que j'ai mentionnée ci-dessus consiste à utiliser un couvrant l'index afin que toutes les colonnes dont vous avez besoin soient incluses dans une structure de données d'index. L'avantage est que toutes vos recherches sont O (log n), et la requête n'a pas besoin de faire une seconde E/S pour lire la ligne physique pour obtenir d'autres colonnes.

Dans ce cas, vous devez créer l'index de recouvrement sur les colonnes token, source, count comme je l'ai mentionné ci-dessus. Essayez également d'allouer suffisamment d'espace de cache pour que l'index puisse être mis en cache dans la mémoire.

+1

+1 pour la bonne approche; mais un tel index serait presque aussi grand que l'ensemble du disque, pensez-vous qu'il serait plus rapide que l'indexation sur token? – Javier

+0

Dépend du nombre de lignes et d'autres facteurs spécifiques au système. La seule façon d'être sûr est de l'essayer avec * votre * base de données et de mesurer la performance. –

+0

C'est une bonne approche mais le seul problème qu'elle crée si vous avez un jeton qui est dans plus d'une source, alors vous obtenez tous ces cas ajoutés ensemble.Par exemple dans mon exemple le jeton "chat" est dans la source 2,3, et 5 donc donc il me donne un compte de 5 au lieu de me donner 2 & 3 avec le compte de 4, 3 & 5 avec un compte de 3, et 2 & 5 avec un compter de 3. Dans mon vrai, grand ensemble de données il y a des jetons qui apparaissent dans presque tous les documents qui me donneraient GROUP_CONCAT de milliers de sources et de leur nombre de respect. – cruzja

1

Si le jeton n'est pas indexé, il devrait l'être.