Я последовал совету Дэвида Гласса, хотя казалось, что у меня есть проблема. Я обновил свой файл package.json, чтобы devDependencies совпал с зависимостями, и затем предупреждение исчезло.
Create your points using Point
values of Geometry
data types in MyISAM
table. As of Mysql 5.7.5, InnoDB
tables now also support SPATIAL
indices.
Create a SPATIAL
index on these points
Use MBRContains()
to find the values:
SELECT *
FROM table
WHERE MBRContains(LineFromText(CONCAT(
'('
, @lon + 10 / ( 111.1 / cos(RADIANS(@lon)))
, ' '
, @lat + 10 / 111.1
, ','
, @lon - 10 / ( 111.1 / cos(RADIANS(@lat)))
, ' '
, @lat - 10 / 111.1
, ')' )
,mypoint)
, or, in MySQL 5.1
and above:
SELECT *
FROM table
WHERE MBRContains
(
LineString
(
Point (
@lon + 10 / ( 111.1 / COS(RADIANS(@lat))),
@lat + 10 / 111.1
),
Point (
@lon - 10 / ( 111.1 / COS(RADIANS(@lat))),
@lat - 10 / 111.1
)
),
mypoint
)
This will select all points approximately within the box (@lat +/- 10 km, @lon +/- 10km)
.
This actually is not a box, but a spherical rectangle: latitude and longitude bound segment of the sphere. This may differ from a plain rectangle on the Franz Joseph Land, but quite close to it on most inhabited places.
Apply additional filtering to select everything inside the circle (not the square)
Possibly apply additional fine filtering to account for the big circle distance (for large distances)
Not a MySql specific answer, but it'll improve the performance of your sql statement.
What you're effectively doing is calculating the distance to every point in the table, to see if it's within 10 units of a given point.
What you can do before you run this sql, is create four points that draw a box 20 units on a side, with your point in the center i.e.. (x1,y1 ) . . . (x4, y4), where (x1,y1) is (givenlong + 10 units, givenLat + 10units) . . . (givenLong - 10units, givenLat -10 units). На самом деле вам нужны только две точки, верхняя левая и нижняя правая назовите их (X1, Y1) и (X2, Y2).
Теперь ваш оператор SQL использует эти точки, чтобы исключить строки, которые определенно больше 10u от вашего для данной точки он может использовать индексы по широте и долготе, поэтому будет на несколько порядков быстрее, чем у вас в настоящее время.
например
select . . .
where locations.lat between X1 and X2
and locations.Long between y1 and y2;
Коробочный подход может возвращать ложные срабатывания (вы можете выбрать точки в углах прямоугольник на расстоянии> 10u от заданной точки), поэтому вам все равно нужно рассчитать расстояние до каждой точки. Однако это снова будет намного быстрее, потому что вы резко ограничили количество точек для тестирования до точек внутри рамки.
Я называю эту технику «Мыслить внутри коробки» :)
РЕДАКТИРОВАТЬ: I don't know where the best place is to build the four points, or how they could be passed to a mySql query in Php. However, once you have the four points, there's nothing stopping you combining your own SQL statement with mine.
select name,
( 3959 * acos( cos( radians(42.290763) )
* cos( radians( locations.lat ) )
* cos( radians( locations.lng ) - radians(-71.35368) )
+ sin( radians(42.290763) )
* sin( radians( locations.lat ) ) ) ) AS distance
from locations
where active = 1
and locations.lat between X1 and X2
and locations.Long between y1 and y2
having distance < 10 ORDER BY distance;
I know with MS SQL I can build a SQL statement that declares four floats (X1, Y1, X2, Y2) and calculates them before the "main" select statement, like I said, I've no idea if this can be done with MySql. However I'd still be inclined to build the four points in C# and pass them as parameters to the SQL query.
Sorry I can't be more help, if anyone can answer the MySQL & Php specific portions of this, feel free to edit this answer to do so.
Проверьте эту презентацию, чтобы получить хороший ответ. В основном она показывает два разных подхода, показанных в комментариях, с подробным объяснением почему / когда следует использовать тот или иной вариант и почему расчет «в коробке» может быть очень интересным.