Курсор MySQL Looping, чтобы найти минимум - PullRequest
0 голосов
/ 16 декабря 2018

Вот задача, которую я пытаюсь выполнить:

Найти ближайший доступный автомобиль для данного местоположения.

У меня есть таблица для vehicle итаблица для location выглядит следующим образом:

CREATE TABLE location
  (location_id numeric(8,0) UNIQUE NOT NULL,
   address varchar(100), 
   latitude float,
   longitude float,
   PRIMARY KEY(location_id)
 );

CREATE TABLE vehicle
  (license_plate char(6) UNIQUE NOT NULL,
   make varchar(30) NOT NULL,
   model varchar(30) NOT NULL,
   year numeric(4,0) NOT NULL CHECK(year>1990),
   state char(2) NOT NULL, 
   capacity int NOT NULL,
   last_location numeric(8,0) DEFAULT NULL,
   FOREIGN KEY(last_location) REFERENCES location(location_id) ON DELETE 
   CASCADE ON UPDATE CASCADE,
   PRIMARY KEY(license_plate)
 );

Я написал запрос, который вызывает функцию для циклического перебора таблицы vehicle, вычисления расстояния с заданным местоположением и возврата license_plate измашина с минимальной дистанцией.

SELECT @locationA := 11111111;
SET @loc_lat = (SELECT latitude FROM location WHERE location_id = 
@locationA);
SET @loc_long = (SELECT longitude FROM location WHERE location_id = 
@locationA);
SELECT license_plate, make, model FROM vehicle
WHERE license_plate = find_car(@loc_lat, @loc_long); 

DELIMITER $$
CREATE FUNCTION find_car(loc_lat float, loc_long float) RETURNS char  
BEGIN
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    DECLARE CUR_DIST float DEFAULT 1000000000; 
    DECLARE car_lat NUMERIC; 
    DECLARE car_long NUMERIC; 
    DECLARE dist float; 
    DECLARE closest_car char(6); 
    DECLARE car_temp char(6);
    DECLARE loc_temp numeric; 
    DECLARE car_cursor CURSOR FOR SELECT license_plate, last_location FROM 
    vehicle;
    OPEN car_cursor; 
    car_loop: LOOP
        FETCH car_cursor INTO car_temp, loc_temp;
        SET car_lat = (SELECT latitude FROM location WHERE location_id = 
        loc_temp);
        SET car_long = (SELECT longitude FROM location WHERE location_id = 
        loc_temp);
        SET dist = (SELECT ST_Distance_Sphere(
            point(loc_lat, loc_long),
            point(car_lat, car_long)
            ) * .000621371192);
        IF dist < CUR_DIST THEN 
            SET closest_car = car_temp;
            SET CUR_DIST = dist; 
        END IF; 
    END LOOP;
    CLOSE car_cursor; 
    RETURN(closest_car);
END $$ 
DELIMITER ;

Сейчас это ничего не возвращает, и я не совсем уверен, почему.Я все еще довольно новичок в SQL, поэтому заранее спасибо!

1 Ответ

0 голосов
/ 16 декабря 2018

Мой совет - сначала попробовать подход, основанный на множествах, и посмотреть, как он работает.

Несколько хороших идей о том, почему можно найти здесь .

Общая стратегия: Для каждого location_id найдите следующую ближайшую location_id

Некоторые конкретные тактики:

  • Использование jpgunter's GETDISTANCE (код найден ниже)
  • Нажмите здесь , чтобы увидеть его код в контексте
  • Рассчитать расстояние между всеми значениями location_id, используя самостоятельное соединение с таблицей location
  • Определить произвольное расстояние, которое «слишком далеко», и исключить все, что находится за пределами этого расстояния
  • Это может помочь улучшить производительность
  • Выберите наименьшее расстояние от результата

Это стартовый скрипт для самостоятельного соединения с таблицей location и критериями "слишком далеко" ...

SELECT l1.location_id as l1_location_id
      ,l1.latitude as l1_latitude
      ,l1.longitude as l1_longitude
      ,l2.location_id as l2_location_id
      ,l2.latitude as l2_latitude
      ,l2.longitude as l2_longitude
      ,GETDISTANCE(l1.latitude, l1.longitude, l2.latitude, l2.longitude) as l1_12_distance
  FROM location AS l1
  JOIN location AS l2 ON l1.location_id <> l2.location_id
 WHERE GETDISTANCE(l1.latitude, l1.longitude, l2.latitude, l2.longitude) <= 1000; -- JJAUSSI: arbitrary "too far"

Это функция jpgunter GETDISTANCE...

DELIMITER $$
/*
Takes two latitudes and longitudes in degrees. You could comment out the conversion if you want to pass as radians.
Calculate the distance in miles, change the radius to the earth's radius in km to get km.
*/

DROP FUNCTION IF EXISTS GETDISTANCE$$
CREATE FUNCTION GETDISTANCE 
  (deg_lat1 FLOAT, deg_lng1 FLOAT, deg_lat2 FLOAT, deg_lng2 FLOAT) 
  RETURNS FLOAT 
  DETERMINISTIC 
BEGIN 
  DECLARE distance FLOAT;
  DECLARE delta_lat FLOAT; 
  DECLARE delta_lng FLOAT; 
  DECLARE lat1 FLOAT; 
  DECLARE lat2 FLOAT;
  DECLARE a FLOAT;

  SET distance = 0;

  /*Convert degrees to radians and get the variables I need.*/
  SET delta_lat = radians(deg_lat2 - deg_lat1); 
  SET delta_lng = radians(deg_lng2 - deg_lng1); 
  SET lat1 = radians(deg_lat1); 
  SET lat2 = radians(deg_lat2); 

  /*Formula found here: http://www.movable-type.co.uk/scripts/latlong.html*/
  SET a = sin(delta_lat/2.0) * sin(delta_lat/2.0) + sin(delta_lng/2.0) * sin(delta_lng/2.0) * cos(lat1) * cos(lat2); 
  SET distance = 3956.6 * 2 * atan2(sqrt(a),  sqrt(1-a)); 

  RETURN distance;
END$$
DELIMITER ;

Возможно, вы захотите пересмотреть имя поля year, так как оно resошибочное слово .

Вы можете обнаружить, что если / когда ваша база данных увеличивается, эти имена являются слишком общими:

  • location
  • address
  • latitude
  • longitude

Однако я не знаю вашу базу данных.Возможно, ваши имена таблиц и полей точно соответствуют вашим потребностям.Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...