mysql8 nearest place using radius filter - mysql

First of all, I am using mysql8.
I have a table that store states of my country.
My table ddl is:
CREATE TABLE state_v2(
`id` int(11) NOT NULL,
uf_code INT NULL,
uf VARCHAR(2) NOT NULL,
`name` VARCHAR(100) NOT NULL,
latitude FLOAT(8) NOT NULL,
longitude FLOAT(8) NOT NULL,
`country` varchar(75) NOT NULL,
coord POINT SRID 4326 NOT NULL,
PRIMARY KEY (id),
unique (country, uf_code),
unique (latitude, longitude),
index (uf_code)
);
ALTER TABLE state_v2 ADD SPATIAL INDEX(coord);
The user choose a place and I use this place as reference to find the nearest state less then 100km. (the parameter is a correct point).
I am using this query:
SELECT * FROM (
SELECT
sv.*,
ST_distance_sphere(
$param,
sv.coord) as distance
FROM state_v2 sv
WHERE ST_distance_sphere(
$param,
sv.coord) < 100000
) as temp
order by temp.distance;
1 - Is it correct this query? I am worried about the performance of using like that, it is using full table scan.
2 - Is it correct the created indexes?

Related

How to look up multipolygon data in mysql by coordinates

// 맵 데이터
db.Exec(`CREATE TABLE IF NOT EXISTS
geo_datas(
id bigint unsigned primary key auto_increment,
level tinyint unsigned NOT NULL,
geom multipolygon NOT NULL,
country_name varchar(30) NOT NULL,
country_code varchar(10) NOT NULL,
name varchar(50),
minx REAL NOT NULL,
miny REAL NOT NULL,
maxx REAL NOT NULL,
maxy REAL NOT NULL
)
`)
Data is created in the table created with the code above. The geom column is a multipolygon type, and the following data is inserted.
MULTIPOLYGON (((5.347486495971623 45.98247528076172, 5.3532729148865315 45.991767883300895, 5.3617901802064125 45.99477386474615, 5.37621974945074 45.99368667602545, 5.380286693572998 45.98820495605469, 5.3969597816467285 45.981689453125, 5.396285533905029 45.97837066650396, 5.378274440765438 45.97423934936529, 5.347486495971623 45.98247528076172)))
After that, I wanted to get the multipolygon data to which the coordinates belong as a map coordinate point type, and I tried the following.
SELECT id
FROM geo_datas
WHERE MBRContains(geom , ST_GeomFromText('Point(37.543238571036824 126.9867128133774)'));
When I executed the above sql, it ran for 201 seconds.
And it returned 0 results.
The data I have is world data, a result of 0 is impossible, I don't know why it returns 0.
The query is also very slow, I don't know how to solve it, please give me some advice, can I have a simple example?
ALTER TABLE geo_datas
ADD SPATIAL(multipolygon);
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS
geo_datas(
id bigint unsigned primary key auto_increment,
level tinyint unsigned NOT NULL,
geom multipolygon NOT NULL SRID 0,
country_name varchar(30) NOT NULL,
country_code varchar(10) NOT NULL,
name varchar(50),
minx REAL NOT NULL,
miny REAL NOT NULL,
maxx REAL NOT NULL,
maxy REAL NOT NULL,
index idx_level (level),
index idx_country_code (country_code),
index idx_country_name (country_name),
index idx_name (name),
spatial index (geom)
)
`)
i changed the table as above code
query := "SELECT level, name, country_name, country_code, ST_AsGeoJSON(geom, 20) geom " +
"FROM geo_datas " +
"WHERE ST_Contains(geom, ST_GeomFromText('Point(%v %v)')) " +
"ORDER BY level asc "
After changing the code as above, everyting working fine.

SQL correct definition of floating points

I am measuring some values over a geo-location
I need to create a table that consists of:
date
longitude
latitude
value
The date column is the date of the measure,
The value is just an int value of the measure,
the longitude and latitude are a coordinates - they are floating points columns with 3 digits before the point and 5 after (i.e. *.*)
I'm wondering how to define my table, I try to use:
CREATE TABLE `obs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` date DEFAULT NULL,
`lon` decimal(5,5) DEFAULT NULL,
`lat` decimal(5,5) DEFAULT NULL,
`val` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Here is a link to sqlFiddle
But When I'm trying to run
INSERT INTO `obs` VALUES (null,'2014/06/07','34.000','31.342',1)
I am getting the following error:
Out of range value for column 'lon' at row 1:
Can anyone explain me what's wrong?
decimal(5,5) means
5 decimal places in TOTAL
5 decimal places after the point
That would make all numbers invalid having a decimal place before the point.
You probably want
decimal(10,5)

MySql How to convert varchar (latitude, longitude) to Decimal fields?

In mysql I have a varchar containing latitude and longitude provided by Google maps.
I need to be able to query based on a bounding box value, but have no need for the geo features now available. I'm trying to populate 2 new Decimal fields with the Decimal values found in the varchar. Here is the query that I'm trying to use, but the result are all rounded values in the new fields.
Sample data:
'45.390746926938185, -122.75535710155964',
'45.416444621636415, -122.63058006763458'
Create Table:
CREATE TABLE IF NOT EXISTS `cameras` (
`id` int(11) NOT NULL auto_increment,
`user_id` int(75) NOT NULL,
`position` varchar(75) NOT NULL,
`latitude` decimal(17,15) default NULL,
`longitude` decimal(18,15) default NULL,
`address` varchar(75) NOT NULL,<br />
`date` varchar(11) NOT NULL,<br />
`status` int(1) NOT NULL default '1',
`duplicate_report` int(11) NOT NULL default '0',
`missing_report` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1050 ;
SQL:
UPDATE cameras
SET latitude = CAST( (substring(position,1,locate(',',position))) AS DECIMAL(17,15) ),
longitude = CAST( (substring(position,locate(',',position)+1)) AS DECIMAL(18,15) )
SQL Alternate attempt:
UPDATE cameras
SET latitude = CONVERT( (substring(position,1,locate(',',position))), DECIMAL(17,15) ),
longitude = CONVERT( (substring(position,locate(', ',position)+1)), DECIMAL(18,15) )
The resulting field values are for both scenarios:
45.000000000000000 and -122.000000000000000
AND
45.000000000000000 and -122.000000000000000
Can anyone see what I'm doing wrong?
Thanks.
Both the CAST and CONVERT forms seem to be correct.
SELECT CAST((SUBSTRING(t.position,1,LOCATE(',',t.position))) AS DECIMAL(17,15)) AS lat_
, CONVERT(SUBSTRING(t.position,LOCATE(', ',t.position)+1),DECIMAL(18,15)) AS long_
FROM (SELECT '45.390746926938185, -122.75535710155964' AS `position`) t
lat_ long_
------------------ ----------------------
45.390746926938185 -122.755357101559640
I think position is a reserved word, but I don't think that matters in this case. But it wouldn't hurt to assign a table alias and qualify all column references
UPDATE cameras c
SET c.latitude = CAST((SUBSTRING(c.position,1,LOCATE(',',c.position))) AS DECIMAL(17,15))
, c.longitude = CAST((SUBSTRING(c.position,LOCATE(',',c.position)+1)) AS DECIMAL(18,15))
But I suspect that won't resolve the problem.
One thing to check is for a before or after update trigger defined on the table, which is rounding/modifying the values assigned to the latitude and longitude columns?
I suggest you try running just a query.
SELECT CAST((SUBSTRING(c.position,1,LOCATE(',',c.position))) AS DECIMAL(17,15)) AS lat_
, CAST((SUBSTRING(c.position,LOCATE(',',c.position)+1)) AS DECIMAL(18,15)) AS lon_
FROM cameras c
and verify that produces the decimal values you expect.
A dot character should be recognized as a decimal point. Does the position column contain some other special characters, like a space or something?
From what you posted, it looks like the CAST and CONVERT working on the integer portion up to the decimal point. (There shouldn't be an implicit convert to signed integer in there, so it's not clear why the characters following the decimal point aren't being included.)
If you can figure out what character(s) are being used to represent the decimal point, then you could use a MySQL REPLACE() function to replace those with a simple dot character.

Find nearest postcode to Latitude Longitude in MySQL

I have a table Postcode which holds all UK postcode (approx 1.8m i think)
CREATE TABLE `Postcode` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Postcode` varchar(8) DEFAULT NULL,
`Postcode_Simple` varchar(8) DEFAULT NULL,
`Positional_Quality_Indicator` int(11) DEFAULT NULL,
`Eastings` int(11) DEFAULT NULL,
`Northings` int(11) DEFAULT NULL,
`Latitude` double DEFAULT NULL,
`Longitude` double DEFAULT NULL,
`LatLong` point DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `Postcode` (`Postcode`),
KEY `Postcode_Simple` (`Postcode_Simple`),
KEY `LatLong` (`LatLong`(25))
) ENGINE=InnoDB AUTO_INCREMENT=1755933 DEFAULT CHARSET=latin1;
What I want to achieve is...Given a co-ordinate, locate the postcode nearest to the co-ordinate. Problem is I'm having a bit of an issue with the query (actually in a stored procedure) I've written to do this. The query is:
SELECT
Postcode
FROM
(SELECT
Postcode,
GLENGTH(
LINESTRINGFROMWKB(
LINESTRING(
LatLong,
GEOMFROMTEXT(CONCAT('POINT(', varLatitude, ' ', varLongitude, ')'))
)
)
) AS distance
FROM
Postcode
WHERE
NOT LatLong IS NULL) P
ORDER BY
Distance
LIMIT
1;
The problem I'm having is that the query takes some 12seconds to run and I cannot have it take that long to get a result. Can anyone think of any ways I can reliably speed this query up?
(Here's the explain for the query)
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL (NULL) (NULL) (NULL) (NULL) 1688034 Using filesort
2 DERIVED Postcode ALL LatLong (NULL) (NULL) (NULL) 1717998 Using where
I've been trying to think of a way to narrow down the initial amount of data that I must perform the distance calculation on, but I haven't been able to come up with anything that doesn't restrict to finding postcodes within a given distance.
Maybe try something along the lines of:
SELECT Postcode, lat, lon
FROM
(
SELECT Postcode, MAX(latitude) AS lat, MAX(longitude) AS lon
FROM PostCode
-- field name
GROUP BY Postcode
HAVING MAX(latitude)<varLatitude AND MAX(longitude)<varLongitude
LIMIT 1
) AS temp
which will basically bring the postcode whose lat and lon are less than the ones you specify but greater than any other lat/lon combination that is less than your vars; so effectively the closest lat/lon to your vars, hence the closest postcode. You can try the same using MIN and greater then instead to go the other way round.
The above will only get you a single result/postcode. If you're looking to have something niftier with like finding a group of postcodes given in a specific radius of lat/long then you should have a look at the formula explained at https://developers.google.com/maps/articles/phpsqlsearch_v3#findnearsql
I've written a tutorial on pretty much exactly what you're after.
Basically, you're on the right lines. In order to improve the efficiency of the search, you'll need to reduce the number of GLength() calculations made by making use of a spatial index on your LatLong field. If you restrict the search to a refined area, such as polygon 10 miles around the point you're comparing the postcodes to, you'll find the query is much quicker.

MySQL GIS / Spatial Extensions - melting my brain

I'm new to GIS in MySQL and it's melting my brain!
I've created a table "places" as so:
CREATE TABLE `places` (
`id` int(6) unsigned zerofill NOT NULL auto_increment,
`business_name` varchar(100) NOT NULL,
`street_postcode` varchar(10) NOT NULL,
`longitude` decimal(22,20) NOT NULL,
`latitude` decimal(22,20) NOT NULL,
`coord` point NOT NULL,
UNIQUE KEY `id` (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=25080 DEFAULT CHARSET=utf8
id, business_name and street_postcode where inserted into the table
I then updated the table with longitude and latitude coordinates in PHP as so:
UPDATE `places` SET `longitude` = '".$longitude."', `latitude` = '".$latitude."', `coord` = GeomFromText('POINT(".$coord.")') WHERE `id` = '".$row->id."' LIMIT 1
This all seems to have gone well, but I'm stuck when trying to find the nearest place to X coordinates. How do I find the 10 nearest places to X longitude and latitude?
This does not seem to work:
SELECT business_name, street_postcode, ROUND(GLength(LineStringFromWKB(LineString(AsBinary(coord), AsBinary('51.49437081 -0.2275573')))))
AS distance FROM places ORDER BY distance ASC LIMIT 10;
Eternal thanks in advance!
That is because the spatial functions are not really implmented in MySQL. They stubbed out all the code but have not implemented all the methods.
I recommend using PostGIS or SpatiaLite.