2010-12-02 4 views
5

Tenir compte de la requête suivante:Optimisation d'une requête SQL pour éviter la table analyse complète

SELECT * FROM Transactions 
WHERE day(Stamp - interval 3 hour) = 1; 

Le Stamp colonne dans la Transactions tableau est un TIMESTAMP et il y a un index sur elle. Comment puis-je modifier cette requête afin d'éviter les analyses de table complètes? (Qui est, en utilisant Stamp en dehors du jours() fonction)

Merci!

+0

Je ne demande pas les "index de fonction" - ils n'existent pas. Plutôt je voudrais transformer cette requête de la même façon que vous pourriez transformer "SELECT * FROM table WHERE sqrt (colonne) = 2" en "SELECT * FROM table WHERE colonne = 4" – emx

Répondre

7

Voilà comment je le ferais:

ajouter quelques champs supplémentaires: année, mois, jour ou même heure, la minute en fonction du trafic que vous attendez. Ensuite, créez un déclencheur pour remplir les champs supplémentaires, en soustrayant peut-être l'intervalle de 3 heures à l'avance. Enfin, construisez un index sur les champs supplémentaires.

+0

Merci, pas une option. – emx

+4

MySQL ne supporte pas les index de fonction - La solution de Massimog est la seule alternative à spécifier toutes les dates possibles - cependant puisque votre requête va probablement extraire 1/30ème des lignes, utiliser une recherche d'index ne sera pas plus rapide qu'avec un balayage complet de la table. – symcbean

+0

Vous pourriez avoir raison sur la considération de la performance - jamais pensé à ce sujet vraiment. Plus intéressé à transformer ma requête. – emx

1

Si le but est juste d'éviter des analyses complètes de table et vous avez une clé primaire (par exemple le nom PK) pour les transactions, envisager d'ajouter l'indice couvrant

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp) 

Puis

SELECT * FROM Transactions WHERE PK IN (SELECT PK FROM Transactions 
WHERE day(Stamp - interval 3 hour) = 1 
) 

Cette requête devrait ne pas utiliser les analyses de table complètes (cependant l'optimiseur peut décider d'utiliser l'analyse complète, si le nombre de lignes dans la table est petit ou pour toute autre raison statistique :))

y be est d'utiliser la table temporaire au lieu de la sous-requête.

0

Calculez votre valeur de timbre souhaitée séparément avant d'exécuter votre requête principale, à savoir

Étape 1 - calculer la valeur du timbre souhaité

Étape 2 - exécuter une requête où Stamp> (valeur calculée)

Parce qu'il n'y a aucun calcul à l'étape 2, vous devriez être en mesure d'utiliser votre index.

+0

Je souhaite aussi, mais cela ne fonctionne pas – ajreal

1

Vous pouvez souvent réécrire la fonction de sorte que vous ayez quelque chose qui ressemble à WHERE Stamp=XXXX et XXXX est une expression. Vous pouvez créer une série d'instructions BETWEEN pour chaque mois, WHERE Stamp BETWEEN timestamp('2010-01-01 00:00:00') AND timestamp ('2010-01-01 23:59:59') OR Stamp BETWEEN ..., mais je ne suis pas certain que cela utiliserait l'index dans ce cas. Je construisais une colonne qui était le jour du mois, comme le suggère @petr.

0

Si je comprends bien, vous voulez essentiellement retourner toutes les lignes où le timbre tombe le premier de chaque mois (après avoir soustrait les 3 heures)? Si (et c'est un gros si), vous avez une fenêtre fixe de, disons les 6 derniers mois, vous pourriez juste énumérer 6 tests de gamme. Mais encore, je ne suis pas sûr que l'accès indexé sera plus rapide de toute façon.

select * 
    from transactions 
where stamp between timestamp '2010-06-01 03:00:00' and timestamp '2010-06-02 02:59:59' 
    or stamp between timestamp '2010-07-01 03:00:00' and timestamp '2010-07-02 02:59:59' 
    or stamp between timestamp '2010-08-01 03:00:00' and timestamp '2010-08-02 02:59:59' 
    or stamp between timestamp '2010-09-01 03:00:00' and timestamp '2010-09-02 02:59:59' 
    or stamp between timestamp '2010-10-01 03:00:00' and timestamp '2010-10-02 02:59:59' 
    or stamp between timestamp '2010-11-01 03:00:00' and timestamp '2010-11-02 02:59:59' 
    or stamp between timestamp '2010-12-01 03:00:00' and timestamp '2010-12-02 02:59:59'; 

NB! Je ne suis pas sûr de savoir comment fonctionne la partie milliseconde de l'horodatage. Vous devrez peut-être le remplir en conséquence.

0

Retravailler petr répond un peu pour éviter la clause IN, et le faire pour MyISAM ou InnoDB.

Pour MyISAM

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp) 

Ou, pour InnoDB, où le PK est implicitement inclus dans chaque index,

ALTER TABLE Transactions ADD INDEX Stamp (Stamp) 

Puis

SELECT * 
FROM Transactions LEFT JOIN 
    (
    SELECT PK 
    FROM Transactions 
    WHERE DAYOFMONTH(Stamp - interval 3 hour) = 1 
) a ON Transactions.PK=a.PK 

Le sous-requête aura un seul indice l'exécution, et la requête externe tirera seulement les lignes de la table où a.PK est passé.

Questions connexes