2009-05-04 6 views
3

Voici ma question:Y at-il quelque chose de plus rapide que "avoir compte" pour les grandes tables?

select word_id, count(sentence_id) 
from sentence_word 
group by word_id 
having count(sentence_id) > 100; 

Le sentenceword table contient 3 champs, wordid, sentenceid et un identifiant de clé primaire. Il a 350k + lignes. Cette requête prend 85 secondes et je me demande (en espérant, en priant?) Il y a un moyen plus rapide de trouver tous les mots qui ont plus de 100 phrases.

J'ai essayé d'extraire la partie de comptage de sélection, en faisant simplement 'avoir count (1)' mais aucune ne l'accélère.

J'apprécierais toute aide que vous pouvez prêter. Merci!

+1

Quel SGBD utilisez-vous? –

+0

Ceci est avec MySQL (et en utilisant HeidiSQL comme client pour y accéder) – Jeff

+0

Une autre clarification ennuyeuse ... (désolé): Les données changent constamment. Environ 10k lignes insérées/jour et ~ 5k lignes supprimées. Donc, je pense que cela rend impossible les résultats stockés ou mis en cache – Jeff

Répondre

3

ayant un compte (phrase_id)> 100;

Il y a un problème avec ceci ... Soit la table a des paires mot/phrase dupliquées, soit ce n'est pas le cas.

Si elle a double mot/paires de phrases, vous devez utiliser ce code pour obtenir la bonne réponse:

HAVING COUNT(DISTINCT Sentence_ID) > 100 

Si la table ne pas en double mot/paires de phrases ... alors vous ne devriez pas compter phrase_ids, vous devriez simplement compter les lignes.

HAVING COUNT(*) > 100 

Dans ce cas, vous pouvez créer un index sur word_id ne, pour des performances optimales.

+0

Incroyable comment quelque chose d'aussi simple qu'un * est la différence entre une requête de 85 secondes et littéralement .3 secondes! Merci à tous- – Jeff

1

Si cette requête est souvent effectuée et que la table est rarement mise à jour, vous pouvez conserver une table auxiliaire avec des identifiants de mot et des comptes de phrase correspondants - difficile à imaginer pour toute optimisation ultérieure!

+4

Vous voulez dire, comme un index? :-) – bignose

6

Si vous n'en possédez pas déjà un, créez un index composite sur phrase_id, word_id.

+0

Je crois que le bon ordre des colonnes pour cet index serait (word_id, phrase_id). –

+0

@Irina C: Je viens de tester sur le serveur SQL avec les colonnes d'index dans les deux sens, et malgré des plans d'exécution différents, la quantité d'E/S logiques était la même. –

1

Votre requête est correcte, mais elle nécessite un peu d'aide (index) pour obtenir des résultats plus rapides.

Je n'ai pas mes ressources à portée de main (ou l'accès à SQL), mais je vais essayer de vous aider de la mémoire. Conceptuellement, la seule façon de répondre à cette requête est de compter tous les enregistrements qui partagent le même ID_mot. Cela signifie que le moteur de recherche a besoin d'un moyen rapide pour trouver ces enregistrements. Sans un index sur word_id, la seule chose que la base de données peut faire est de parcourir la table un enregistrement à la fois et de continuer à faire des totaux de chaque mot_id distinct qu'elle trouve. Cela nécessiterait généralement une table temporaire et aucun résultat ne peut être distribué tant que la table entière n'a pas été analysée. Pas bon.

Avec un index sur word_id, il faut encore passer par la table, donc vous penseriez que cela n'aiderait pas beaucoup. Toutefois, le moteur SQL peut maintenant calculer le nombre de chaque ID_mot sans attendre la fin de la table: il peut répartir la ligne et le nombre de cette valeur de ID_cot (s'il passe votre clause where) ou ignorer la ligne (si ce n'est pas le cas); cela entraînera une charge de mémoire plus faible sur le serveur, éventuellement des réponses partielles, et la table temporaire n'est plus nécessaire. Un deuxième aspect est le parallélisme; avec un index sur word_id, SQL peut diviser le travail en segments et utiliser des cœurs de processeur séparés pour exécuter la requête en parallèle (en fonction des capacités matérielles et de la charge de travail existante).

Cela pourrait suffire à votre requête; mais vous devrez essayer de voir:

CREATE INDEX someindexname ON sentence_word (word_id) 

(syntaxe T-SQL, vous n'avez pas spécifié quel produit SQL que vous utilisez)

Si cela ne suffit pas (ou ne vous aide pas du tout), il existe deux autres solutions.

D'abord, SQL vous permet de précalculer COUNT (*) en utilisant des vues indexées et d'autres mécanismes. Je n'ai pas les détails sous la main (et je ne le fais pas souvent). Si vos données ne changent pas souvent, cela vous donnera des résultats plus rapides mais avec un coût en complexité et un peu de stockage.

En outre, vous pouvez envisager de stocker les résultats de la requête dans une table distincte.C'est pratique seulement si les données ne changent jamais, ou les changements sur un horaire précis (par exemple, pendant une actualisation de données à 2 heures du matin), ou si cela change très peu et vous pouvez vivre avec des résultats non parfaits pendant quelques heures devrait prévoir une actualisation périodique des données); c'est l'équivalent moral de l'entrepôt de données d'un pauvre. Le meilleur moyen de savoir avec certitude ce qui fonctionne pour vous est d'exécuter la requête et d'examiner le plan de requête avec et sans certains index candidats comme celui ci-dessus.

+0

Merci pour la réponse utile! En fait, tous les champs pertinents sont déjà indexés ... Mais il suffisait de changer la requête pour compter (*) au lieu de compter (phrase_id). Nuit et jour. Donc, ça semble être ça. Je pense que pour une raison quelconque j'ai supposé que count() avec un champ spécifique était plus efficace qu'avec *, mais cela semble maintenant être une supposition idiote. Va faire quelques vérifications supplémentaires pour confirmer que c'était le problème. – Jeff

0

Il est, de façon surprenante, d'une manière encore plus rapide pour y parvenir sur de grands ensembles de données:

SELECT totals.word_id, totals.num 
    FROM (SELECT word_id, COUNT(*) AS num FROM sentence_word GROUP BY word_id) AS totals 
WHERE num > 1000; 
Questions connexes