对查询建立索引

时间:2009-02-12 15:03:54

标签: sql

我的查询花费的时间太长而且经常超时。它是基于邻近度的邮政编码搜索表值函数。无论如何都要根据查询进行索引,所以它不必每次重新计算所有这些值吗?邮政编码和邮政编码列表合计超过一百万行。

这是表函数。

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

3 个答案:

答案 0 :(得分:3)

这需要我几毫秒,也许你的方法是错误的看看这里:SQL Server Zipcode Latitude/Longitude proximity distance search 2000/2005版本

或在2008版本中使用地理数据类型:SQL Server 2008 Proximity Search With The Geography Data Type

答案 1 :(得分:2)

我建议在经度和纬度上使用多列索引。

使用边界框很好,这通常可以加快查询速度。根据我提到的索引,您应该看到巨大的改进。

在旁注中,您的纬度/经度存储在十进制(16,12)中。 12位数的精度可能超过您的需要。第五位(以纬度/长度为单位)表示大约3英尺。所以......第12位可能实际上代表纳米(或更小)。通过使用较小的数据类型,您的表(和索引)将更有效。对于邮政编码搜索尤其如此,因为您拥有的纬度/长度是一个代表邮政编码中心的点,位置不是很精确。对于经度,我通常使用十进制(8,5)。由于纬度通常在-90到90的范围内,因此您可以使用十进制(7,5)来获得纬度。

答案 2 :(得分:1)

您可以尝试在索引上强制INDEX JOIN,看看它是否有帮助:

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