2012-02-17 2 views
5

Je vais avoir un MySQL-table comme ceci:Comment indexer deux colonnes de date pour ce genre de requête

CREATE TABLE `dates` (
`id` int UNSIGNED NULL AUTO_INCREMENT , 
`object_id` int UNSIGNED NOT NULL , 
`date_from` date NOT NULL , 
`date_to` date NULL , 
`time_from` time NULL , 
`time_to` time NULL , 
PRIMARY KEY (`id`) 
); 

qui est interrogé la plupart du temps de cette façon:

SELECT object_id FROM `dates` 
WHERE NOW() BETWEEN date_from AND date_to 

Comment puis-je index la meilleure table? Devrais-je créer deux index, un pour date_from et un pour date_to ou un index combiné sur les deux colonnes est-il préférable?

+0

Je pense que date_from est préférable de créer un index au lieu de combiner –

+0

Vous [probablement] ne vous sentez pas bien. Dites qu'il y a 10 lignes pour certains objets. 8 ont une date de fin dans le passé, 1 est "courant", et 1 est "futur". Combien de ceux-ci sont filtrés par "NOW()> date_from" (réponse: un seul) et combien sont filtrés par "NOW()

Répondre

4

Pour la requête:

WHERE NOW() >= date_from 
    AND NOW() <= date_to 

Un indice de composé (date_from, date_to) est inutile.

Créez les deux index: (date_from) et (date_to) et laissez l'optimiseur SQL décider à chaque fois lequel utiliser. En fonction des valeurs et de la sélectivité, l'optimiseur peut choisir l'un ou l'autre index. Ou aucun d'entre eux. Il n'y a pas de moyen facile de créer un index qui prendra en compte les deux conditions.


(Un index spatial pourrait être utilisé pour optimiser une telle condition, si vous pouviez traduire les dates en latitude et en longitude).

Mise à jour

Mon erreur. Un index sur (date_from, date_to, object_id) peut et est effectivement utilisé dans certaines situations pour cette requête. Si la sélectivité du NOW() <= date_from est suffisamment élevée, l'optimiseur choisit d'utiliser cet index plutôt que de faire un scan complet sur la table ou d'utiliser un autre index. En effet, il s'agit d'un index de couverture, ce qui signifie qu'aucune donnée n'est nécessaire pour être extraite de la table, seule la lecture des données de l'index est requise.

Note mineure (non liée à la performance, à l'exactitude de la requête seulement). Votre condition est équivalente à:

WHERE CURRENT_DATE() >= date_from 
    AND (CURRENT_DATE() + INTERVAL 1 DAY <= date_to 
     OR (CURRENT_DATE() = NOW() 
     AND CURRENT_DATE() = date_to 
      ) 
    ) 

Êtes-vous sûr que vous voulez que ou voulez-vous ceci:

WHERE CURRENT_DATE() >= date_from 
    AND CURRENT_DATE() <= date_to 

La fonction NOW() retourne un DATETIME, tandis que CURRENT_DATE() retourne un DATE, sans la partie du temps.

+0

Merci pour votre réponse - fondamentalement, j'ai deux types de requête: soit je sélectionne par 'object_id' pour obtenir toutes les dates liées pour mon objet ou en choisissant une plage de dates où un jour sélectionné (soit' NOW() 'ou n'importe quelle autre date) est entre 'date_from' et' date_to' donc j'obtiens toutes les lignes qui arrivent ce jour-là. – acme

0

Créer un index avec (date_from, date_to) comme cet indice unique serait utilisable pour les critères WHERE

Si vous créez des index séparés alors MySQL devra utiliser l'un ou l'autre au lieu des deux

1

Combien de lignes par rapport à la taille de votre table votre requête retourne-t-elle? Si c'est plus de 10% je ne serais pas la peine de créer un index, dans ce cas, vous êtes tout près d'un scan de table de toute façon. S'il est bien inférieur à 10%, alors dans ce cas, utiliser un index contenant (date_from, date_to, object_id) afin que le résultat de la requête puisse être entièrement construit à partir des informations de l'index, sans que la base de données ait besoin de revenir à les données de la table pour obtenir la valeur de object_id. En fonction de la taille de votre table, celle-ci peut utiliser beaucoup d'espace. Si vous pouvez épargner cela, essayez-le.

+0

Je ne savais pas que le champ sélectionné est alors pris de l'index, bon à savoir! Je suppose que le nombre de lignes est à cinq chiffres et que les correspondances sont inférieures à 10%. Donc, cela semble être le chemin à parcourir. – acme

2

Vous devez créer un index couvrant date_from, date_to et object_id comme expliqué par ypercube. L'ordre des champs dans l'index dépend si vous aurez plus de données pour le passé ou le futur. Comme l'a souligné Erwin en réponse au commentaire de Sanjay, le champ date_to sera plus sélectif si vous avez plus de dates dans le passé et vice versa.

CREATE INDEX ON (date_to, date_from, object_id); 
+0

Ok, merci de le signaler! – acme

Questions connexes