我是sql的新手而且我被卡住了。我试图计算每个用户旅行的距离(年度)总和。我有一个表(让我们称之为dist_table)具有以下结构:
rowid user_name date LAT LONG
1 maria 2005-01-01 51.555 5.014
2 maria 2005-01-01 51.437 5.474
3 peter 2005-02-03 51.437 5.474
4 john 2005-02-03 51.858 5.864
5 maria 2005-02-04 51.858 5.864
6 john 2005-02-03 51.437 5.474
7 john 2006-02-04 0 0
8 john 2006-02-04 51.858 5.864
9 john 2006-02-04 51.858 5.864
10 john 2006-02-04 51.437 5.474
这是计算的中间步骤(只是为了澄清我的意思):
rowid user_name date LAT LONG distance
1 maria 2005-01-01 51.555 5.014 0
2 maria 2005-01-01 51.437 5.474 34.452
3 peter 2005-02-03 51.437 5.474 0
4 john 2005-02-03 51.858 5.864 0
5 maria 2005-02-04 51.858 5.864 54.012
6 john 2005-03-03 51.437 5.474 54.012
7 john 2006-02-04 0 0
8 john 2006-02-04 51.858 5.864 54.012
9 john 2006-02-04 51.858 5.864 0
10 john 2006-02-04 51.437 5.474 54.012
这是我需要的最终结果:
user_name date sum(distance)
maria 2005 88.464
peter 2005 0
john 2005 54.012
john 2006 108.024
我正在考虑使用这个公式(Haversine)来计算连续行之间的距离,然后总结它:
SELECT user_name,date,dist_table.LAT,dist_table.LONG, 6373 * 2 * ASIN(SQRT(POWER(SIN((orig_latitude - abs(next_latitude)) * pi()/180 / 2),2)
+ COS(orig_latitude * pi()/180) * COS(abs(next_latitude) * pi()/180) * POWER(SIN((orig_longitude - next_longitude) * pi()/180 / 2),2) ))
AS distance FROM dist_table WHERE dist_table.LAT !=0 AND dist_table.LONG !=0;
但是,我无法弄清楚如何调用连续行。到目前为止,这是我在试图找出如何连接行时得到的结果:
SELECT user_name, date, LAT,
IFNULL( (
SELECT MAX( LAT )
FROM dist_table
WHERE user_name = t1.user_name
AND ( date < t1.date )
) ,0) AS next_latitude
FROM dist_table AS t1 ORDER BY user_name, date;
问题是,对于每个用户,可以有多行满足此条件,并选择最大值而不是前一个值。此外,经度和/或纬度有时为0,我需要忽略这些行。
我想这可能已经解决了,如果我首先使用基于user_name和date的行顺序创建列,然后在条件中添加类似date + 1 = t1.date的内容。不幸的是,我对我使用的服务器的权限非常有限,所以这可能必须用用户定义的变量来处理,但我不知道该怎么做。
我使用的是mysql 5.6.19-log。
有人可以帮助我吗?
答案 0 :(得分:2)
所以这是问题第一部分的解决方案......
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,user_name VARCHAR(12) NOT NULL
,date DATE NOT NULL
,LAT DECIMAL(5,3) NOT NULL
,LON DECIMAL (5,2) NOT NULL
);
INSERT INTO my_table VALUES
( 1,'maria','2005-01-01',51.555 ,5.014),
( 2,'maria','2005-01-01',51.437 ,5.474),
( 3,'peter','2005-02-03',51.437 ,5.474),
( 4,'john' ,'2005-02-03',51.858 ,5.864),
( 5,'maria','2005-02-04',51.858 ,5.864),
( 6,'john' ,'2005-02-03',51.437 ,5.474),
( 7,'john' ,'2006-02-04',0 ,0),
( 8,'john' ,'2006-02-04',51.858 ,5.864),
( 9,'john' ,'2006-02-04',51.858 ,5.864),
(10,'john' ,'2006-02-04',51.437 ,5.474);
SELECT x.user_name
, x.id from_id
, MIN(y.id) to_id
FROM my_table x
JOIN my_table y
ON y.user_name = x.user_name
AND y.id > x.id
WHERE (y.lat <> 0 AND y.lon <> 0)
AND (x.lat <> 0 AND x.lon <> 0)
GROUP
BY x.id;
+-----------+---------+-------+
| user_name | from_id | to_id |
+-----------+---------+-------+
| maria | 1 | 2 |
| maria | 2 | 5 |
| john | 4 | 6 |
| john | 6 | 8 |
| john | 8 | 9 |
| john | 9 | 10 |
+-----------+---------+-------+
对于问题的其余部分,以下内容应该有效。
我的数据库中有一个名为geo_distance_km的函数。它看起来像这样,并且每次都可以节省输入半正式公式:
delimiter //
create DEFINER = CURRENT_USER 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 ;
我们可以将它与我们已经拥有的东西结合起来......
SELECT user_name
, YEAR(date) year
, COALESCE(SUM(distance),0) total
FROM
( SELECT a.*
, b.lat to_lat
, b.lon to_lon
, ROUND(geo_distance_km(from_lat,from_lon,b.lat,b.lon),3) distance
FROM
( SELECT x.user_name
, x.date
, x.id from_id
, x.lat from_lat
, x.lon from_lon
, MIN(y.id) to_id
FROM my_table x
LEFT
JOIN my_table y
ON y.user_name = x.user_name
AND y.id > x.id
AND (y.lat <> 0 OR y.lon <> 0)
WHERE (x.lat <> 0 AND x.lon <> 0)
GROUP
BY x.id
) a
LEFT
JOIN my_table b
ON b.id = a.to_id
) n
GROUP
BY user_name
, year;
+-----------+------+---------+
| user_name | year | total |
+-----------+------+---------+
| john | 2005 | 108.024 |
| john | 2006 | 54.012 |
| maria | 2005 | 88.464 |
| peter | 2005 | 0.000 |
+-----------+------+---------+
我不太明白你如何处理重叠多年的距离,但这应该让你接近你所追求的目标。