Comment optimiser une requête pour qu'elle recherche d'abord un index, puis un autre index après cela


12

J'ai deux ensembles de mesures terrestres à partir de données satellites, chacun avec des champs temporels (mjd pour la date moyenne julienne) et des positions géographiques (GeoPoint, spatial) et je cherche des coïncidences entre les deux ensembles de sorte que leurs temps correspondent à un seuil de 3 heures (ou 0,125 jours) et leurs distances à moins de 200 km les uns des autres.

J'ai fait des index pour les champs mjd sur les tables et les tables spatiales.

Lorsque je viens de me joindre à la contrainte de temps, la base de données calcule 100 000 correspondances en 8 secondes et calcule les distances pour les 100 000 correspondances de cette période. La requête ressemble à ceci:

select top 100000 h.Time, m.Time, h.GeoPoint.STDistance(m.GeoPoint)/1000.0
from L2V5.dbo.header h join L2.dbo.MLS_Header m
on h.mjd between m.mjd-.125 and m.mjd+.125
option( table hint ( h, index(ix_MJD) ), table hint( m, index(ix_MJD) ) )

Et le plan exécuté est:

Seule contrainte mjd

Une fois triées, 9 des distances étaient inférieures à 200 km, il y a donc des matchs. Le problème est que lorsque j'ajoute la contrainte de distance et l'exécute à la place,

select top 10 h.Time, m.Time, h.GeoPoint.STDistance(m.GeoPoint)/1000.0
from L2V5.dbo.header h join L2.dbo.MLS_Header m
on h.mjd between m.mjd-.125 and m.mjd+.125
and h.GeoPoint.STDistance(m.GeoPoint)<200000
option( table hint ( h, index(ix_MJD) ), table hint( m, index(ix_MJD) ) )

ça part longtemps. De toute évidence, en 8 secondes, il a pu trouver 100 000 matchs dans le temps, dont 9 sous 200 km, l'optimiseur doit donc essayer quelque chose de sous-optimal. Le plan ressemble à ci-dessus avec un filtre sur les distances (je suppose).

avec construction spatiale, pas de filtre spatial

Je peux forcer l'utilisation de l'index spatial avec ceci:

select top 5 h.Time, m.Time, h.GeoPoint.STDistance(m.GeoPoint)/1000.0 
from L2V5.dbo.header h join L2.dbo.MLS_Header m 
on h.GeoPoint.STDistance(m.GeoPoint)<200000
and h.mjd between m.mjd-.125 and m.mjd+.125 
option( table hint ( h, index(ix_MJD), index(ix_GeoPoint) ), table hint( m, index(ix_MJD) ) )

les deux contraintes avec les deux index

ce qui prend ensuite 3 minutes pour trouver 5 correspondances.

Comment puis-je dire à l'optimiseur de requête d'utiliser d'abord l'index MJD, puis l'index spatial ensuite (ou est-ce déjà ce qu'il fait) et existe-t-il un moyen de l'aider en lui indiquant le nombre de correspondances à attendre? S'il peut calculer 100 000 matchs avec des distances en 8 secondes qui ont 9 à moins de 200 km, l'ajout de l'indice spatial ne devrait-il pas le rendre plus rapide et non plus lent?

Merci pour tout autre conseil ou idée.

EDIT: Pour répondre à la question à quoi ressemble le plan sans les indices, ceci (et cela prend une éternité):

pas d'indices

Il vaut peut-être aussi la peine de mentionner qu'il y a presque 1 million d'enregistrements dans une table et 8 millions dans l'autre


À quoi ressemble votre plan de requête si vous supprimez ces indices?
Zane

@Zane, j'ai édité le post et ajouté le plan de requête sans conseil. Il remplace la recherche par des scans et le timing est épouvantable.
user261963

Réponses:


6

Le problème est qu'il pourrait (et connaissant probablement les indices spatiaux) supposera que le filtre spatial sera beaucoup plus sélectif que le filtre temporel.

Mais si vous avez quelques millions d'enregistrements à moins de 200 km, cela pourrait être bien pire.

Vous lui demandez de trouver des enregistrements dans un rayon de 200 km, ce qui renvoie des données classées par ordre spatial. Trouver les enregistrements qui sont proches dans le temps signifie vérifier chacun d'eux.

Ou bien vous recherchez des enregistrements par heure et vous obtenez des résultats dans l'ordre chronologique. Ensuite, filtrer cette liste dans un rayon de 200 km consiste à vérifier chacune d'elles.

Si vous filtrez les données dans deux plages comme celle-ci, il devient difficile d'appliquer le deuxième filtre à l'aide d'un index. Il vaut peut-être mieux lui dire de ne pas utiliser l'index spatial si le filtre temporel est le plus serré.

Si les deux sont grands individuellement, et ce n'est qu'ensemble qu'ils sont serrés, alors vous avez un problème plus complexe, un problème que les gens ont essayé de résoudre depuis longtemps, et qui pourrait être bien résolu par des index couvrant la 3D (et au-delà) espace. Sauf que SQL Server n'en a pas.

Pardon.

Edit: plus d'infos ...

Il s'agit d'un problème similaire à la recherche de plages de temps couvrant un moment particulier. Lorsque vous recherchez les enregistrements qui commencent avant ce point, vous avez alors un désordre non ordonné des heures de fin - et vice-versa. Si vous recherchez dans l'annuaire des personnes dont le nom de famille commence par F, vous ne pouvez pas espérer trouver très facilement les personnes dont le prénom commence par R. Et un index sur le prénom n'aide pas non plus pour la même raison. Il est difficile de trouver des choses dans le prochain index lorsque votre premier index n'est pas une égalité.

Maintenant, si vous pouviez changer votre filtre de date en un filtre d'égalité (ou une série de filtres d'égalité), vous pourriez avoir une chance, sauf qu'un index spatial est un type spécial d'index et ne peut pas être utilisé comme deuxième niveau dans un indice composite.

Vous vous retrouvez donc dans une situation délicate, je le crains. :(

Modifier: Essayez:

select top 100000 h.Time, m.Time, h.GeoPoint.STDistance(m.GeoPoint)/1000.0
from L2V5.dbo.header h join L2.dbo.MLS_Header m
on h.mjd between m.mjd-.125 and m.mjd+.125
where h.GeoPoint.STDistance(m.GeoPoint)/1000.0 < 200
option( table hint ( h, index(ix_MJD) ) );

Notez que je brise délibérément la sargabilité en divisant par 1000 avant de comparer à 200. Je veux que ce travail soit effectué dans la recherche de clé.

Attention, vous pourriez éviter d'avoir besoin des recherches (et des astuces) en INCLUANT GeoPoint et Time dans les deux index ix_MJD. Cela éliminera certainement une partie de la chaleur du plan de requête.


Je ne sais pas si cela change quoi que ce soit, mais le filtre temporel est beaucoup plus sélectif.
user261963

D'accord. Est-il donc acceptable de localiser toutes les lignes assorties dans le temps, puis de vérifier chaque emplacement sans l'index?
Rob Farley

... alors le plan ressemble à votre plan d'origine, mais a un prédicat ou un filtre supplémentaire.
Rob Farley

Suggéré quelques changements avec une modification rapide. Vous n'avez pas besoin de faire allusion à m, juste h. Bien que si vous pouvez échanger celui auquel vous ajoutez 1/8, pour vous assurer que vous modifiez la colonne du plus petit tableau et utilisez ces valeurs pour rechercher dans le plus grand, cela vous aidera également. Si h est 8M et m est 1M, laissez le prédicat BETWEEN et ne faites que h. Si c'est l'inverse, changez votre prédicat et votre indice (mais mieux que de changer l'indice est d'ajouter ces colonnes à votre index).
Rob Farley

Supprimer tous les indices de table semble fonctionner mieux à la fin, tant que je fais h entre m et non l'inverse. La requête n'utilise plus du tout les index GeoPoint, mais elle ne les utilisait pas de toute façon efficacement. J'ai inclus la colonne GeoPoint dans l'index MJD et cela m'a beaucoup aidé. select top 10000 h.Time, m.Time, m.GeoPoint.STDistance(h.GeoPoint), h.mjd-m.mjd from L2V5.dbo.header h join L2.dbo.MLS_Header m on m.GeoPoint.STDistance(h.GeoPoint)<200000 and m.mjd between h.mjd-.125 and h.mjd+.125 order by h.mjd
user261963
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.