2009-02-12 8 views
1

J'ai une requête qui prend trop de temps et expire fréquemment. C'est une fonction table-valeur de recherche de code postal basée sur la proximité. Est-il possible d'indexer en fonction de la requête, de sorte qu'il n'est pas nécessaire de recalculer toutes ces valeurs à chaque fois? Le code postal et la liste des codes postaux combinés représentent plus d'un million de lignes.Indexation sur une requête

Voici la fonction de la table.

Create FUNCTION [dbo].[ZipsInRadius] (@zipCode varchar(15), 
    @radius int, @unit char(1)) 
RETURNS @areaResults TABLE(
    Zip varchar (30), 
    City varchar (255), 
    St varchar (20), 
    Lat decimal (16,12), 
    Long decimal (16,12))  
BEGIN 

    DECLARE @iStartLat decimal(16, 12) 
    DECLARE @iStartLong decimal(16, 12) 
    SELECT 
     @iStartLat = CAST(Latitude AS decimal(16, 12)), 
     @iStartLong = CAST(Longitude AS decimal(16, 12)) 
    FROM zip 
    WHERE zipcode LIKE @zipCode + '%' 
    SELECT 
     @iStartLat = CAST(Latitude AS decimal(16, 12)), 
     @iStartLong = CAST(Longitude AS decimal(16, 12)) 
    FROM postalcode 
    WHERE postalcode LIKE @zipCode + '%' 
    DECLARE @latRange decimal(16, 12) 
    DECLARE @longRange decimal(16, 12) 

    IF (@unit = 'K')   --Get distance in kilometers 
     BEGIN 
      SELECT @LatRange = 
       (CAST(@radius/((6076.0/5280.0) * 60.0) 
       AS decimal(16, 12))) * 0.621371 
      SELECT @LongRange = 
       (@radius/(((cos(@iStartLat * pi()/180.0) * 6076.0) 
       /5280.0) * 60)) * 0.621371 
     END 
    ELSE      --Get distance in miles (the default) 
     BEGIN 
      SELECT @LatRange = CAST(@radius/((6076.0/5280.0) * 60.0) 
       AS decimal(16, 12)) 
      SELECT @LongRange = 
       @radius/(((cos(@iStartLat * pi()/180.0) * 6076.0) 
       /5280.0) * 60) 
     END 

    DECLARE @lowLatitude decimal(16, 12) 
    DECLARE @highLatitude decimal(16, 12) 
    DECLARE @lowLongitude decimal (16, 12) 
    DECLARE @highLongitude decimal (16, 12) 
    SELECT @lowLatitude = @iStartLat - @latRange 
    SELECT @highLatitude = @iStartLat + @latRange 
    SELECT @lowLongitude = @iStartLong - @longRange 
    SELECT @highLongitude = @iStartLong + @longRange 

    INSERT INTO @areaResults (zip, city, st, lat, long) 
     SELECT ZIPcode, CITY, STate, LATitude, LONGitude 
     FROM Zip Z 
     WHERE Z.Latitude <= @highLatitude 
        AND Z.Latitude >= @lowLatitude 
      AND Z.Longitude >= @lowLongitude 
        AND Z.Longitude <= @highLongitude  
     INSERT INTO @areaResults (zip, city, st, lat, long) 
     SELECT postalcode, CITY, province, LATitude, LONGitude 
     FROM postalcode z 
     WHERE Z.Latitude <= @highLatitude 
        AND Z.Latitude >= @lowLatitude 
      AND Z.Longitude >= @lowLongitude 
        AND Z.Longitude <= @highLongitude 
    RETURN 
END 

Répondre

2

Je voudrais recommande un index à plusieurs colonnes sur la longitude et la latitude.

Il est bon que vous utilisiez un cadre de sélection, ce qui accélère habituellement votre requête. Avec l'indice que je mentionne, vous devriez voir d'énormes améliorations. Sur une note de côté, vous avez vos latitude/longitudes enregistrées dans un Decimal (16,12). 12 chiffres de précision est probablement plus que ce dont vous avez besoin. Le cinquième chiffre (en unités lat/long) représente environ 3 pieds. donc .. le 12ème chiffre pourrait effectivement représenter des nanomètres (ou moins). En utilisant un type de données plus petit, vos tables (et index) seront plus efficaces. Cela est particulièrement vrai avec les recherches de code postal car les lat/longs que vous avez sont un point représentant le centre d'un code postal, la position n'est pas très précise pour commencer. Pour la longitude, j'utilise habituellement Decimal (8,5). Comme la latitude est généralement comprise entre -90 et 90, vous pouvez vous en sortir avec la décimale (7,5) pour la latitude.

+0

grâce ... J'ai ajouté les index, mais je encore des problèmes. le zip en question retourne effectivement 98000 lignes (son à Toronto), et prend 41 secondes à courir ... – Shawn

+0

malade hve pour essayer la chose décimale, il semble que les deux tables ont des types différents, c'est probablement faire une tonne de conversions – Shawn

+0

Finalement, j'ai juste ajouté un index sur le zip et puis interne le rejoindre sur la table que je cherchais en premier. il a réduit la fonction de valeur de table par beaucoup – Shawn

1

Vous pouvez essayer de forcer INDEX JOIN sur vos index et voir si elle aide:

CREATE INDEX ix_zip_lat ON zip(lat) 

CREATE INDEX ix_zip_long ON zip(long) 

SELECT * FROM zip 
WITH (INDEX(ix_zip_lat), INDEX (ix_zip_long)) 
WHERE lat BETWEEN @lowlat and @hilat 
     AND long BETWEEN @lowlong and @hilong