2

For caching reasons I need to write the Km (kilometers) field back to the location table, so far I was able to calculate the Km. Everything I am trying so far (update with unions, subqueries, etc) gives me the SQL Error 1093.

In MySQL,how do I update the Km value back to the table?

This is the table schema

CREATE TABLE `locations` (
  `epoch_date` int(10) unsigned DEFAULT NULL,
  `latitude` float NOT NULL,
  `longitude` float NOT NULL,
  `km` float DEFAULT NULL
);

INSERT INTO `locations` (`epoch_date`, `latitude`, `longitude`, `km`) VALUES
(1429913506, -8.7285, 119.401, NULL),
(1429913631, -9.1279, 117.67, NULL),
(1429945707, -8.7063, 119.36, NULL),
(1431929523, -8.5745, 119.707, NULL),
(1431941343, -8.5773, 119.713, NULL),
(1431958383, -8.5881, 119.724, NULL),
(1431969963, -8.589, 119.728, NULL),
(1431998403, -8.5766, 119.724, NULL);

This is the select query that generates the KM (kilometer)

SELECT
    latitude
    , longitude
    , epoch_date
    , @latitude2 :=
    (
        SELECT
            latitude
        FROM locations loc1
        WHERE
            loc1.epoch_date < loc.epoch_date
        ORDER BY epoch_date DESC LIMIT 1 OFFSET 0
    ) as prev_latitude
    , @longitude2 :=
    (
        SELECT
            longitude
        FROM locations loc1
        WHERE
            loc1.epoch_date < loc.epoch_date
        ORDER BY epoch_date DESC LIMIT 1 OFFSET 0
    ) as prev_longitude
    , (ACOS(COS(RADIANS(90-latitude)) *COS(RADIANS(90-@latitude2)) +SIN(RADIANS(90-latitude)) *SIN(RADIANS(90-@latitude2)) *COS(RADIANS(longitude-@longitude2))) *6371) as km
FROM locations loc
ORDER BY epoch_date

Here is a link to the SQL Fiddle http://sqlfiddle.com/#!9/7f95de/2/0

dinnouti
  • 1,707
  • 2
  • 15
  • 21
  • Possible duplicate of [MySQL Error 1093 - Can't specify target table for update in FROM clause](http://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause) – Solarflare Jun 08 '16 at 21:42

2 Answers2

3

First, your use of variables in the select statement is not guaranteed to work. MySQL does not guarantee the ordering of expressions in a select, so the variables could be assigned in any order. Depending on the order of evaluation is dangerous.

Once you solve that problem (probably by using a subquery), you can update the km column by using a subquery:

update locations l join
       (<your query here>) u
       on l.latitude = u.latitude and l.longitude = u.longitude
    set l.km = u.km;

Oh, I see something else dangerous as well. You are using floating point numbers for the representation of lat and long. You should really be using decimals, typically something like decimal(10, 6) is sufficient for this purpose.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
1

You could set up functions to return the prev_latitude and pre_longitude and use that in your UPDATE query.

Something like this

DELIMITER $$
CREATE FUNCTION prev_latitude(epoch INT)
  RETURNS FLOAT
  LANGUAGE SQL
BEGIN
  DECLARE lat FLOAT;
  SET lat = null;

  SELECT latitude INTO lat
  FROM locations
  WHERE epoch_date < epoch
  ORDER BY epoch_date DESC LIMIT 1 OFFSET 0;

  RETURN lat;
END;
$$
DELIMITER ;

And

DELIMITER $$
CREATE FUNCTION prev_longitude(epoch INT)
  RETURNS FLOAT
  LANGUAGE SQL
BEGIN
  DECLARE lon FLOAT;
  SET lon = null;

  SELECT longitude INTO lon
  FROM locations
  WHERE epoch_date < epoch
  ORDER BY epoch_date DESC LIMIT 1 OFFSET 0;

  RETURN lon;
END;
$$
DELIMITER ;

And your update query can now be written as

UPDATE locations
SET km = (ACOS(COS(RADIANS(90-latitude)) *COS(RADIANS(90-prev_latitude(epoch_date))) +SIN(RADIANS(90-latitude)) *SIN(RADIANS(90-prev_latitude(epoch_date))) *COS(RADIANS(longitude-prev_longitude(epoch_date)))) *6371);
theCaveat
  • 399
  • 1
  • 9