2011-06-30 3 views
4

Comment faire en sorte que cette requête s'exécute plus rapidement ...?Comment faire en sorte que cette requête s'exécute plus vite?

 
SELECT account_id, 
      account_name, 
      account_update, 
      account_sold, 
      account_mds, 
      ftp_url,   
      ftp_livestatus, 
      number_digits, 
      number_cw, 
      client_name, 
      ppc_status, 
      user_name 
FROM  
     Accounts, 
     FTPDetails, 
     SiteNumbers, 
     Clients, 
     PPC, 
     Users 

WHERE Accounts.account_id = FTPDetails.ftp_accountid 
AND  Accounts.account_id = SiteNumbers.number_accountid 
AND  Accounts.account_client = Clients.client_id  
AND  Accounts.account_id = PPC.ppc_accountid 
AND  Accounts.account_designer = Users.user_id 
AND  Accounts.account_active = 'active' 
AND  FTPDetails.ftp_active = 'active' 
AND  SiteNumbers.number_active = 'active' 
AND  Clients.client_active = 'active'  
AND  PPC.ppc_active = 'active' 
AND  Users.user_active = 'active' 
ORDER BY 
     Accounts.account_update DESC 

Merci à l'avance :)

EXPLIQUER résultats de la requête:

first part of table

second part of table

Je n'ai pas vraiment de clés étrangères mis en place ... J'essayais d'éviter d'apporter des modifications à la base de données car je devrais faire un Révision bientôt.

seules les clés primaires sont l'identifiant de chaque table, par ex. account_id, ftp_id, ppc_id ...

+1

Quels indices avez-vous sur les tables? –

+1

Publiez vos index et la sortie EXPLAIN pour les personnes à évaluer. Sinon, ils devineront juste. – jishi

+1

... et le plan EXPLAIN, et le nombre de lignes pour chaque table et la cardinalité des index – symcbean

Répondre

3

Utilisez EXPLAIN pour savoir quel index pourrait être utilisé et quel index est réellement utilisé. Créez un index approprié si nécessaire.

Si FTPDetails.ftp_active a uniquement les deux entrées valides 'active' et 'inactive', utilisez BOOL comme type de données.

Comme une note de côté: je suggère fortement d'utiliser jointures explicites au lieu de celles implicites:

SELECT 
    account_id, account_name, account_update, account_sold, account_mds, 
    ftp_url, ftp_livestatus, 
    number_digits, number_cw, 
    client_name, 
    ppc_status, 
    user_name 
FROM Accounts 
INNER JOIN FTPDetails 
    ON Accounts.account_id = FTPDetails.ftp_accountid 
    AND FTPDetails.ftp_active = 'active' 
INNER JOIN SiteNumbers 
    ON Accounts.account_id = SiteNumbers.number_accountid 
    AND SiteNumbers.number_active = 'active' 
INNER JOIN Clients 
    ON Accounts.account_client = Clients.client_id 
    AND Clients.client_active = 'active' 
INNER JOIN PPC 
    ON Accounts.account_id = PPC.ppc_accountid 
    AND PPC.ppc_active = 'active' 
INNER JOIN Users 
    ON Accounts.account_designer = Users.user_id 
    AND Users.user_active = 'active' 
WHERE Accounts.account_active = 'active' 
ORDER BY Accounts.account_update DESC 

Cela rend la requête plus lisible, car la condition de jointure est proche du nom de la table qui est jointe .

+0

des thats 2 secondes plus vite :) j'ai couru l'expliquer, mais pas sûr de ce que les informations me dit réellement? ou comment l'utiliser ... – JPickup

+0

@JPickup: Postez ici le plan d'explication, ajoutez-le dans la question. –

0

EXPLAIN, de comparer différentes options. Pour commencer, je suis sûr que plusieurs requêtes seront plus rapides que ce monstre. Tout d'abord, parce que l'optimiseur de requêtes passera beaucoup de temps à examiner quel ordre de jointure est le meilleur (5! = 120 possibilités). Et deuxièmement, les requêtes telles que SELECT ... WHERE ....active = 'active' seront mises en cache (bien que cela dépend de la quantité de données modifiées).

+0

J'ai utilisé plusieurs instructions jusqu'à maintenant, mais on m'a demandé d'ajouter la fonction pour que les utilisateurs filtrent les résultats de n'importe quelle valeur ... et ne connaissent pas de moyen de le faire sans sélectionner TOUTES les données dans une seule sélectionnez une déclaration? – JPickup

+0

Bon optimiseurs ne porte pas sur toutes les possibilités - qui pour cette requête serait beaucoup plus que 120 (il est non seulement l'ordre de faire JOIN mais la méthode à utiliser pour chaque REJOIGNEZ ainsi que les index à utiliser plus dans quel ordre pour vérifier les conditions). –

+0

@ypercube bonnes optimiseurs sont [configurable] (http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_optimizer_search_depth). – DrTyrsa

4

Index

  • Vous avez besoin - au moins - un index sur tous les domaines qui est utilisé dans un état JOIN.

  • Les index sur les champs qui apparaissent dans les clauses WHERE ou GROUP BY ou ORDER BY sont le plus souvent utiles. Lorsque dans un tableau, deux ou plusieurs champs sont utilisés dans JOIns (ou WHERE ou GROUP BY ou ORDER BY), un index composé (combiné) de ces champs (deux ou plusieurs) peut être meilleur que des index séparés. Par exemple, dans le tableau SiteNumbers, les index possibles sont le composé (number_accountid, number_active) ou (number_active, number_accountid). La condition dans les champs qui sont booléens (ON/OFF, actif/inactif) ralentit parfois les requêtes (car les index ne sont pas sélectifs et ne sont donc pas très utiles). Restructuration (père normalisation) les tables est une option dans ce cas, mais vous pouvez probablement éviter la complexité ajoutée.


Outre les conseils habituels (examiner le plan EXPLIQUER, ajouter des index en cas de besoin, les variations de test de la requête),

Je remarque que dans votre requête il y a un produit cartésien partielle. La table Accounts a des relations un-à-plusieurs avec trois tables FTPDetails, SiteNumbers et PPC. Cela a pour effet que si vous avez par exemple 1000 comptes, et que chaque compte est lié, disons, à 10 FTPDetails, 20 SiteNumbers et 3 PPC, la requête retournera pour chaque compte 600 lignes (le produit de 10x20x3). Au total 600K lignes où de nombreuses données sont dupliquées.

Vous pouvez à la place diviser la requête en trois plus un pour les données de base (tables de comptes et de repos). De cette façon, seulement 34K lignes de données (ayant une longueur plus petite) serait transférée:

Accounts JOIN Clients JOIN Users 
    (with all fields needed from these tables) 
    1K rows 

Accounts JOIN FTPDetails 
    (with Accounts.account_id and all fields from FTPDetails) 
    10K rows 

Accounts JOIN SiteNumbers 
    (with Accounts.account_id and all fields from SiteNumbers) 
    20K rows 

Accounts JOIN PPC 
    (with Accounts.account_id and all fields from PPC) 
    3K rows 

puis utilisez les données des 4 requêtes du côté client pour afficher les informations combinées.



J'ajouterais les indices suivants:

Table Accounts 
    index on (account_designer) 
    index on (account_client) 
    index on (account_active, account_id) 
    index on (account_update) 

Table FTPDetails 
    index on (ftp_active, ftp_accountid) 

Table SiteNumbers 
    index on (number_active, number_accountid) 

Table PPC 
    index on (ppc_active, ppc_accountid) 
+0

pourquoi ne pas 'Tableau client Indice sur (client_id, client_active)' et 'Tableau des utilisateurs index sur (user_id, user_active)'? – Ron

+1

@sunrong Je ne me souviens pas pourquoi je n'ai ajouté aucune suggestion d'index pour 'Clients' et' Users'. En relisant ma réponse et la question, je pense avoir traité les (clients et utilisateurs) différemment parce qu'ils semblent avoir une relation 1-à-1 avec les comptes et non 1-à-plusieurs. Les index devraient aider là aussi. –

0

L'un de vos principaux problèmes est ici: x.y_active = 'active'

Problème: faible cardinalité
Le champ actif est un champ booléen avec 2 valeurs possibles, en tant que tel il a une très faible cardinalité. MySQL (ou n'importe quel SQL n'utilisera pas d'index si 30% ou plus des lignes ont la même valeur).
Forcer l'index est inutile car cela rendra votre requête plus lente, pas plus rapide.

Solution: partitionner vos tables
Une solution consiste à partitionner vos tables sur les colonnes active.
Cela exclura tous les champs non actifs et fera select agir comme si vous avez réellement un index de travail sur les champs xxx-active.

Sidenote
S'il vous plaît ne jamais utiliser implicite where rejoint, il est beaucoup trop d'erreurs et consufing être utile.
Utilisez une syntaxe similaire à Oswald's answer à la place.

Liens:
Cardinalité: http://en.wikipedia.org/wiki/Cardinality_(SQL_statements)
Cardinalité et index: http://www.bennadel.com/blog/1424-Exploring-The-Cardinality-And-Selectivity-Of-SQL-Conditions.htm
partitionnement de MySQL: http://dev.mysql.com/doc/refman/5.5/en/partitioning.html

+0

Y a-t-il des livres/documents de lecture que vous suggéreriez sur la conception de la base de données et les déclarations SQL? – JPickup

Questions connexes