sql查询可根据与用户的接近程度投放广告

时间:2018-10-29 12:05:06

标签: php mysql geolocation distance proximity

好的,所以我正在网站上实施广告系统。我目前根据会员在注册时输入的城市来存储他们的纬度/经度坐标。每个广告都可以选择提供地理坐标并指定以英里为单位的半径(尽管我也想选择以KM指定)。

Sample tables
--------------users
user_id = 7
name = Charles
lat = xxxxxx.xx
lon = xxxxxx.xx

--------------ads
ad_id = 1121
advertiser_id = 42
ad_type = 728x90
proximity = 50
lat = xxxxxx.xx
lon = xxxxxx.xx

我有这段代码可以搜索我的用户数据库并返回指定距离内的用户。我认为我可以对其进行编辑,以便它可以满足我的需求。但是,我不想显示所有在指定距离内的用户,而是希望显示广告自己指定的距离内的广告,因为一个广告商可能会指定与另一个广告商不同的距离,等等。

这是我使用的原始用户接近度查询:

SELECT users.user_id
     , ( 3959 * acos( cos( radians('".$lat."') ) * 
                      cos( radians( users.lat ) ) * 
                      cos( radians( users.lng ) - 
                           radians('".$lng."') ) + 
                      sin( radians('".$lat."') ) * 
                      sin( radians( users.lat ) ) 
                    ) ) AS distance 
  FROM users 
 WHERE account_type = '1' 
   AND `active` = '1' 
HAVING distance <= '".$dist."'"

所以我想,如果我首先创建一个查询来获取与区域匹配并具有接近性要求的广告,然后选择其纬度/经度并选择当前用户的地理坐标,然后......我可以似乎没有把我的头缠起来。然后,我将根据每个与区域匹配的广告的不同的接近度设置执行一些计算。如果一个广告想要向100英里内的人展示,那么计算结果将与只向20英里内的人展示广告的那个人不同。可能有一种更简单的方法。查询构建器应用程序可以帮助创建此内容吗?

也许是这样吗?

SELECT ads.ad_id
     , ( 3959 * acos( cos( radians('".$user_lat."') ) * 
                      cos( radians( ads.lat ) ) * 
                      cos( radians( ads.lng ) - 
                           radians('".$user_lng."') ) + 
                      sin( radians('".$user_lat."') ) * 
                      sin( radians( ads.lat ) ) 
                    )) AS distance 
  FROM ads 
 WHERE ad_type = '728x90' 
   AND `active` = '1' 
HAVING distance <= ads.proximity"

2 个答案:

答案 0 :(得分:0)

我想出的最终查询解决方案是:

SELECT ads.ad_id
     , proximity AS P, ( 3959 * acos( cos( radians('".$usr_lat."') ) * 
                                      cos( radians( ads.lat ) ) * 
                                      cos( radians( ads.lon ) - 
                                           radians('".$usr_lon."') ) + 
                                      sin( radians('".$usr_lat."') ) * 
                                      sin( radians( ads.lat ) ) 
                                    ) ) AS distance 
FROM ads 
WHERE ads.type = '728x90' 
AND ads.active = '1' 
HAVING distance <= P 
OR P='0' 
ORDER BY distance ASC

我添加了OR P ='0'来投放未指定接近度的广告。

答案 1 :(得分:0)

在进行所有数学运算之前,您还可以进一步限制必须计算的数据量。

当将纬度/经度值限制为附近的值时,可以限制计算距离所需的数据范围。 (公式from these slides (page 12ff)会在您按英里数计算时起作用...否则,您必须用正确的公里数调整“ 69”。

1° of latitude ~= 69 miles 
1° of longitude ~= cos(latitude)*69

要计算较小矩形的经度和纬度时,可以使用(上面的变量):

$lon1 = $lon - $dist / abs(cos(deg2rad($lat))*69);
$lon2 = $lon + $dist / abs(cos(deg2rad($lat))*69);
$lat1 = $lat - ($dist / 69);
$lat2 = $lat + ($dist / 69);

然后您像这样修改查询:

SELECT ads.ad_id
     , proximity AS P, ( 3959 * acos( cos( radians('".$usr_lat."') ) * 
                                      cos( radians( ads.lat ) ) * 
                                      cos( radians( ads.lon ) - 
                                           radians('".$usr_lon."') ) + 
                                      sin( radians('".$usr_lat."') ) * 
                                      sin( radians( ads.lat ) ) 
                                    ) ) AS distance 
FROM ads 
WHERE ads.type = '728x90' 
AND ads.active = '1' 
AND ads.lon BETWEEN $lon1 AND $lon2 
AND ads.lat BETWEEN $lat1 AND $lat2
HAVING distance <= P 
OR P='0' 
ORDER BY distance ASC

这将大大提高整体速度,因为您仅使用了所有坐标的子集。

还有一点要注意:如果您将纬度/经度/距离作为数字字段,则不要在值之间使用'',因为这会触发隐式转换,这可能也需要一些时间...