Consider the following:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,location_id INT NOT NULL
,city VARCHAR(12) NOT NULL
,score INT NOT NULL
,UNIQUE KEY(location_id,city)
);
INSERT INTO my_table VALUES
(1,1,'Berlin',5),
(2,2,'Berlin' ,4),
(3,3,'Berlin' ,2),
(4,1,'Hamburg' ,6),
(5,2,'Hamburg' ,9),
(6,3,'Hamburg' ,1),
(7,1,'Stuttgart' ,7),
(8,2,'Stuttgart' ,6),
(9,3,'Stuttgart' ,3);
SELECT * FROM my_table WHERE location_id = 1 ORDER BY score LIMIT 1;
+----+-------------+--------+-------+
| id | location_id | city | score |
+----+-------------+--------+-------+
| 1 | 1 | Berlin | 5 |
+----+-------------+--------+-------+
In practice, something like the following would be more useful...
DROP TABLE IF EXISTS cities;
CREATE TABLE cities
(city_id SERIAL PRIMARY KEY
,city VARCHAR(12) NOT NULL UNIQUE
,lat DECIMAL(7,4) NOT NULL
,lon DECIMAL(7,4) NOT NULL
);
INSERT INTO cities VALUES
(101,'Berlin',52.5200,13.4050),
(102,'Hamburg',53.5511, 9.9937),
(103,'Stuttgart',48.7758, 9.1829);
DROP TABLE IF EXISTS locations;
CREATE TABLE locations
(location_id SERIAL PRIMARY KEY
,location_name VARCHAR(100) NOT NULL UNIQUE
,lat DECIMAL(7,4) NOT NULL
,lon DECIMAL(7,4) NOT NULL
);
INSERT INTO locations VALUES
(1,'Signal Iduna Park',51.4926,7.4519),
(2,'Allianz Arena',48.2188,11.6247),
(3,'Olympiastadion Berlin',52.5147,13.2395);
I have a stored function for calculating distances on Earth in km...
DELIMITER $$
CREATE FUNCTION geo_distance_km(lat1 double, lon1 double, lat2 double, lon2 double) RETURNS double
begin
declare R int DEFAULT 6372.8;
declare phi1 double;
declare phi2 double;
declare d_phi double;
declare d_lambda double;
declare a double;
declare c double;
declare d double;
set phi1 = radians(lat1);
set phi2 = radians(lat2);
set d_phi = radians(lat2-lat1);
set d_lambda = radians(lon2-lon1);
set a = sin(d_phi/2) * sin(d_phi/2) +
cos(phi1) * cos(phi2) *
sin(d_lambda/2) * sin(d_lambda/2);
set c = 2 * atan2(sqrt(a), sqrt(1-a));
set d = R * c;
return d;
end$$
DELIMITER ;
And so to the query...
SELECT l.location_name
, c.city nearest_city
FROM locations l
JOIN cities c
JOIN ( SELECT x.location_id
, MIN(geo_distance_km(x.lat,x.lon,y.lat,y.lon)) dist
FROM locations x
JOIN cities y
GROUP
BY x.location_id
) n
ON n.location_id = l.location_id
AND geo_distance_km(l.lat,l.lon,c.lat,c.lon) = n.dist;
+-----------------------+--------------+
| location_name | nearest_city |
+-----------------------+--------------+
| Olympiastadion Berlin | Berlin |
| Signal Iduna Park | Hamburg |
| Allianz Arena | Stuttgart |
+-----------------------+--------------+
..and if we add two more cities to that list...
INSERT INTO cities VALUES
(104,'Munich',48.1351,11.5820),
(105,'Dortmund',51.5136,7.4653);
SELECT l.location_name
, c.city nearest_city
FROM locations l
JOIN cities c
JOIN ( SELECT x.location_id
, MIN(geo_distance_km(x.lat,x.lon,y.lat,y.lon)) dist
FROM locations x
JOIN cities y
GROUP
BY x.location_id
) n
ON n.location_id = l.location_id
AND geo_distance_km(l.lat,l.lon,c.lat,c.lon) = n.dist;
+-----------------------+--------------+
| location_name | nearest_city |
+-----------------------+--------------+
| Olympiastadion Berlin | Berlin |
| Allianz Arena | Munich |
| Signal Iduna Park | Dortmund |
+-----------------------+--------------+
We can further optimize this by using bounding boxes, but that's for another day.