У меня есть база данных, полная двумерных данных — точек на карте. Каждая запись имеет поле типа геометрии. Что мне нужно сделать, так это передать точку хранимой процедуре, которая возвращает k ближайших точек (k также будет передано в sproc, но это легко). Я нашел запрос по адресу http://blogs.msdn.com/isaac/archive/2008/10/23/nearest-neighbors.aspx, который получает единственного ближайшего соседа, но я не могу понять, как расширить его, чтобы найти k ближайшие соседи.
Это текущий запрос: T
— таблица, g
— поле геометрии, @x
— точка для поиска, Numbers
— таблица с целыми числами от 1 до n:
DECLARE @start FLOAT = 1000;
WITH NearestPoints AS
(
SELECT TOP(1) WITH TIES *, T.g.STDistance(@x) AS dist
FROM Numbers JOIN T WITH(INDEX(spatial_index))
ON T.g.STDistance(@x) < @start*POWER(2,Numbers.n)
ORDER BY n
)
SELECT TOP(1) * FROM NearestPoints
ORDER BY n, dist
Внутренний запрос выбирает ближайшую непустую область, а внешний запрос затем выбирает лучший результат из этой области; внешний запрос можно легко изменить на (например) SELECT TOP(20)
, но если ближайший регион содержит только один результат, вы застряли с этим.
Я полагаю, что мне, вероятно, нужно рекурсивно искать первую область, содержащую k записей, но без использования табличной переменной (что вызовет проблемы с обслуживанием, поскольку вам нужно создать структуру таблицы, и она может измениться - там много полей), я не вижу, как.
int
(хотя я этого не вижу) 26.03.2010POWER
возвращает тип данных своего первого аргумента (константа 2 интерпретируется какINT
). Изменил мой запрос, чтобы отразить это. 26.03.2010k
, например. 40, вы получаете 40 очков обратно. но он возвращает дубликаты, если точки разбросаны по нескольким регионам, потому что регион n содержит все точки в регионе n -1. конечно, их можно потом отфильтровать (в записях есть поле ID), но тогда вы получите меньше k баллов! Однако это выглядит быстрее, чем использованиеTOP
! 26.03.2010ON
в соединении должен учитывать нижнюю и верхнюю границы, а не только верхнюю границу). 26.03.2010POWER(2, 0)
равно 1, поэтому вы никогда не учитываете точки, которые меньше значения@float
. Второе предложение должно бытьAND (Numbers.n - 1 = 0 OR T.g.STDistance(@x) >= @start*POWER(@p,Numbers.n - 1))
, чтобы обойти это. Однако я обнаружил еще одну проблему: поскольку результаты внутреннего запроса не упорядочены по расстоянию, вы можете получить плохие результаты. например при поиске вокруг (0,0): если@start
равно 100, а число указывает на (0,0) ›@k
, точки на (1,1) могут быть возвращены ошибочно, поскольку они находятся в диапазоне@start
. 29.03.2010OVER
на(ORDER BY n, location.eastNorthGeom.STDistance(@g))
, но это дает арифметические ошибки переполнения - можно ли сортировать по столбцу с плавающей запятой? 29.03.2010INT
- опять же, это не сразу очевидно.n
не требуется вORDER BY
функции ранжирования, поэтому я удалил его в приведенном выше коде. Я также включил ваше исправление для первого диапазона расстояний. 29.03.2010POWER(@p,Numbers.n)
на самом деле переполняетсяfloat
. Просто нужно былоWHERE numbers.n < [sensible number]
в конце внутреннего запроса. 29.03.2010