简体   繁体   中英

How to update a column from a subquery in the same table?

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

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.

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM