Eu tenho uma tabela com uma geometry
coluna. Para um registro, há apenas um Point
armazenado. Um índice espacial foi criado, mas as consultas que procuram o local mais próximo não usam esse índice, resultando em desempenho ruim.
Exemplo de roteiro:
--Create the table
create table Location(
LocationID int not null identity(1,1) primary key,
LocationPoint geometry
)
--add records
declare @counter int =0
WHILE @counter<150000
BEGIN
set nocount on
--select
set @counter =@counter +1
declare @RandomLocation geometry=geometry::Point(RAND() *1000, RAND() *1000, 0)
insert into Location(LocationPoint) values (@RandomLocation)
END
--create index
CREATE SPATIAL INDEX SPATIAL_StructureBE ON dbo.Location(LocationPoint)
USING GEOMETRY_GRID WITH (
BOUNDING_BOX = (xmin = 0.0, ymin = 0.0, xmax = 1000, ymax = 1000),
GRIDS = ( LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM),
CELLS_PER_OBJECT = 16,
STATISTICS_NORECOMPUTE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
--Search, this query should use the index but it doesn't
declare @CurrentLocation geometry=geometry::Point(24,50, 0)
select top 1 *
from Location
order by LocationPoint.STDistance(@CurrentLocation) asc
Correto. Índices espaciais não são aproveitados nessa situação, infelizmente.
Os índices espaciais fornecem um conjunto de grades, permitem que o sistema identifique geometrias (ou geografias) que se sobrepõem a essas grades.
Sua melhor aposta é definir um limite de proximidade aceitável e tentar isso usando algo como STBuffer. STIntersects funciona bem e você pode aumentar esse limite se nada for encontrado (por exemplo, usando um segundo OUTER APPLY se nada for encontrado.
Edit: Eles fazem agora! Verifique minha postagem sobre isso ... http://blogs.lobsterpot.com.au/2014/08/14/sql-spatial-getting-nearest-calculations-working-properly/
Em resumo, para que seu índice seja usado, você precisa ter certeza de que seu valor ORDER BY não pode ser NULL - basta adicionar uma cláusula WHERE...IS NOT NULL e ela deve funcionar.
Como você pode ver aqui: http://technet.microsoft.com/en-us/library/bb895373%28v=sql.105%29.aspx , não apenas o número de métodos que podem usar um índice espacial é limitado, só pode ser usado em uma cláusula WHERE ou JOIN ON.
Você está tentando usar o método STDistance em uma cláusula ORDER BY, onde o uso de índice não é suportado.
EDITAR:
Você pode criar uma tabela de distâncias, unindo-a (portanto - teoricamente - usando o índice espacial) e, em seguida, ordenando as colunas da tabela unida.
Algo a'la ... INNER JOIN [distâncias] ON LocationPoint.STDistance(@CurrentLocation)<[distances].[maxdistance] AND NOT(LocationPoint.STDistance(@CurrentLocation)<[distances].[mindistance])
(Usando 2 colunas, então você não precisa necessariamente escalar linearmente)