用纬度和经度计算距离的性能很差

Fel*_*rlo 1 google-bigquery google-cloud-platform

我试图从特定位置获得2公里或更少的人数,为此我正在计算纬度和经度的距离.在一张桌子中,我只有纬度,经度,而在另一张桌子里,我有更多的田地,但也有纬度和经度.

  • 表1 = 488792行
  • 表2 = 63003行

该查询有效,运行时将处理12.3 MB.

我正在使用的查询是:

select 
e.lat,
e.long,
e.searches,
count(distinct l.id)
from dataset.table1 e
join dataset.table2 l 
     on 6371000*ACOS(COS(3.14159265359/180*(90-e.lat)) *COS(3.14159265359/180*(90-l.lat)) +SIN(3.14159265359/180*(90-e.lat)) *SIN(3.14159265359/180*(90-l.lat)) *COS(3.14159265359/180*(e.long-l.long))) <= 2000 # way to calculate distance from lats and longs
group by e.lat,
e.long,
e.searches
Run Code Online (Sandbox Code Playgroud)

但是查询没有运行,每次都需要超过15分钟,我必须取消.

可能是什么问题呢?

Fel*_*ffa 5

此查询与原始查询类似,需要2分钟:

SELECT distance, COUNT(*) FROM (
SELECT
  e.lat,
  e.long
  , 6371000*ACOS(COS(3.14159265359/180*(90-e.lat)) *COS(3.14159265359/180*(90-l.lat)) +SIN(3.14159265359/180*(90-e.lat)) *SIN(3.14159265359/180*(90-l.lat)) *COS(3.14159265359/180*(e.long-l.long))) <= 2000 distance
  , e.long-l.long longlong, e.lat-l.lat latlat
FROM
  `buoyant-history-159518.test_lat_long.table1` e
JOIN
  `buoyant-history-159518.test_lat_long.table1` l
ON 
 (COS(3.14159265359/180*(90-e.lat)) *COS(3.14159265359/180*(90-l.lat)) +SIN(3.14159265359/180*(90-e.lat)) *SIN(3.14159265359/180*(90-l.lat)) *COS(3.14159265359/180*(e.long-l.long))) <= COS(2000/6371000) + 4.5E-8
)
GROUP BY distance
Run Code Online (Sandbox Code Playgroud)

为了防止浮点错误,我不得不改变JOIN不等式:

6371000*ACOS(COS(3.14159265359/180*(90-e.lat)) *COS(3.14159265359/180*(90-l.lat)) +SIN(3.14159265359/180*(90-e.lat)) *SIN(3.14159265359/180*(90-l.lat)) *COS(3.14159265359/180*(e.long-l.long))) 
<= 2000
Run Code Online (Sandbox Code Playgroud)

相似的:

(COS(3.14159265359/180*(90-e.lat)) *COS(3.14159265359/180*(90-l.lat)) +SIN(3.14159265359/180*(90-e.lat)) *SIN(3.14159265359/180*(90-l.lat)) *COS(3.14159265359/180*(e.long-l.long))) <= COS(2000/6371000) + 4.5E-8)
<= COS(2000/6371000) + 4.5E-8
Run Code Online (Sandbox Code Playgroud)

现在的问题是我们如何才能获得比2分钟更好的表现?让我们在JOIN中添加一些'健全'过滤器 - >同一区域中没有2个点可以在拉特之间有一个距离,长度超过0.something:

SELECT distance, COUNT(*) FROM (
SELECT
  e.lat,
  e.long
  , (COS(3.14159265359/180*(90-e.lat)) *COS(3.14159265359/180*(90-l.lat)) +SIN(3.14159265359/180*(90-e.lat)) *SIN(3.14159265359/180*(90-l.lat)) *COS(3.14159265359/180*(e.long-l.long))) <= COS(2000/6371000) distance
  , e.long-l.long longlong, e.lat-l.lat latlat
FROM
  `buoyant-history-159518.test_lat_long.table1` e
JOIN
  `buoyant-history-159518.test_lat_long.table1` l
ON 
 NOT (e.long=l.long AND e.lat=l.lat) 
 AND ABS(e.long-l.long) < 0.021 #sanity JOIN check
 AND ABS(e.lat-l.lat) < 0.018 #sanity JOIN check
)
GROUP BY distance
Run Code Online (Sandbox Code Playgroud)

有了这个,我们得到非常相似的结果,但在12秒而不是2分钟.

我无法优化您的确切查询,因为您的示例表没有相同的数字或行或列 - 但在进行完整的CROSS JOIN之前尝试应用这些"健全JOIN检查".