项目整体概述

项目的核心是一个数据库查询脚本,其主要功能是依据不同的服务类型,从多个数据库表(poi、user、team)中查询相关记录,并计算这些记录所代表的地点与指定城市之间的距离。最终,筛选出距离小于等于给定值的记录。该项目可用于在地理信息系统中查找特定范围内的兴趣点、用户或团队。

核心代码

// 定义计算距离的函数
function calculateDistance($city_lat, $lng1, $lat1) {
    return "ROUND((6378.137 * acos(cos(radians($city_lat)) * cos(radians($lat1)) * cos(radians($lng1) - radians($city_lng)) + sin(radians($city_lat)) * sin(radians($lat1)))),2)";
}

// 检查必要变量是否定义
if (!isset($serviceType, $city_lat, $city_lng, $db, $listDistance)) {
    die("必要变量未定义");
}

// 确保 $listDistance 是有效的数值
if (!is_numeric($listDistance)) {
    die("listDistance 必须是一个有效的数值");
}

if (!$serviceType || $serviceType === "服务类型") {
    $distance = calculateDistance($city_lat, 'poi_longitude', 'poi_latitude');
    $innerSql = "
        (SELECT 
            poi_token,
            poi_type,
            poi_longitude,
            poi_latitude,
            poi_amount,
            $distance AS poi_distance,
            'poi' AS type 
        FROM " . $db->table("poi") . "
        )
        UNION ALL
        (
            SELECT 
                poi_token,
                'u' AS poi_type,
                user_longitude AS poi_longitude,
                user_latitude AS poi_latitude,
                user_roles AS poi_amount,
                ".calculateDistance($city_lat, 'user_longitude', 'user_latitude')." AS poi_distance,
                'user' AS type 
            FROM " . $db->table("user") . " 
            WHERE user_roles = 1
        )
        UNION ALL
        (SELECT 
            poi_token,
            't' AS poi_type,
            team_longitude AS poi_longitude,
            team_latitude AS poi_latitude,
            team_grade AS poi_amount,
            ".calculateDistance($city_lat, 'team_longitude', 'team_latitude')." AS poi_distance,
            'team' AS type 
        FROM " . $db->table("team") . "
        )";
} else if ($serviceType === "追回类型") {
    $distance = calculateDistance($city_lat, 'poi_longitude', 'poi_latitude');
    $innerSql = "SELECT 
            poi_token,
            poi_type,
            poi_longitude,
            poi_latitude,
            poi_amount,
            $distance AS poi_distance,
            'poi' AS type 
        FROM " . $db->table("poi") . " LIMIT 200";
} else if ($serviceType === "追回团队") {
    $distance = calculateDistance($city_lat, 'team_longitude', 'team_latitude');
    $innerSql = "SELECT 
            poi_token,
            't' AS poi_type,
            team_longitude AS poi_longitude,
            team_latitude AS poi_latitude,
            team_grade AS poi_amount,
            $distance AS poi_distance,
            'team' AS type 
        FROM " . $db->table("team") . " 
        LIMIT 200";
} else {
    $distance = calculateDistance($city_lat, 'user_longitude', 'user_latitude');
    $innerSql = "SELECT 
            poi_token,
            'u' AS poi_type,
            user_longitude AS poi_longitude,
            user_latitude AS poi_latitude,
            user_roles AS poi_amount,
            $distance AS poi_distance,
            'user' AS type 
        FROM " . $db->table("user") . " 
        WHERE user_roles = 1 
        LIMIT 200";
}

// 把内部查询作为子查询,并添加 HAVING 子句
$sql = "SELECT * FROM ($innerSql) AS subquery HAVING poi_distance <= $listDistance";

// 执行查询并处理错误
$row = $db->queryall($sql);
if (!$row) {
    // 处理查询错误
    echo "查询出错: ". $db->error;
}

优势

  1. 灵活性高
    通过 $serviceType 变量,可以灵活切换不同的查询逻辑,满足多种业务需求。例如,既可以进行多表联合查询,也可以单独查询某个表。
  2. 可扩展性强
    定义了 calculateDistance 函数来计算距离,方便代码的复用和扩展。如果后续需要修改距离计算方式,只需修改该函数即可。
  3. 错误处理完善
    在代码中对必要变量进行了检查,确保变量已定义且类型正确。同时,在执行查询时对可能出现的错误进行了处理,方便调试和排查问题。

劣势

  1. SQL 注入风险
    虽然对 $listDistance 进行了数值检查,但直接将变量嵌入 SQL 语句,仍然存在 SQL 注入的潜在风险。攻击者可能通过构造特殊的输入值来改变 SQL 语句的原意,从而获取或篡改数据库中的数据。
  2. 性能问题
    多表联合查询:使用 UNION ALL 合并多个查询结果,当数据量较大时,会增加数据库的处理负担,导致查询性能下降。
    距离计算:在 SQL 查询中使用复杂的距离计算公式,每次查询都需要进行大量的三角函数运算,也会影响查询性能。
  3. 代码复杂度高
    由于需要根据不同的服务类型构建不同的查询语句,代码中存在较多的条件判断和重复代码,增加了代码的维护难度。
  4. 缺乏缓存机制
    每次执行查询时都会直接访问数据库,没有使用缓存机制。如果相同的查询条件频繁出现,会造成不必要的数据库开销。

@漏刻有时