sql >> データベース >  >> RDS >> Mysql

地理距離MySQL

    doubleを使用しています latitudeを保存する およびlongitude 。さらに、1つのポイントのみを見るときに事前計算可能なすべての値を(トリガーによって)事前計算します。現在、使用している数式にアクセスできません。後で追加します。これは、最適な速度/精度のバランスのために最適化されています。

    定義されたエリア検索(x km内のすべてのポイントを指定)の場合、lat/lng値に1e6を掛けた値を追加で保存します (1,000,000)したがって、非常に高速な整数範囲を比較することで、正方形に制限できます。例:

    lat BETWEEN 1290000 AND 2344000
    AND
    lng BETWEEN 4900000 AND 4910000
    AND
    distformularesult < 20
    

    編集:

    PHPの現在の場所の値の定式化と事前計算は次のとおりです。

    WindowSizeは、操作する必要のある値です。度係数1e6であり、中心の周りの正方形で可能な結果を​​絞り込み、結果の検索を高速化するために使用されます。これは少なくともである必要があることを忘れないでください。 検索範囲のサイズ。

    $paramGeoLon = 35.0000 //my center longitude
    $paramGeoLat = 12.0000 //my center latitude
    
    $windowSize = 35000;   
    
    $geoLatSinRad = sin( deg2rad( $paramGeoLat ) );
    $geoLatCosRad = cos( deg2rad( $paramGeoLat ) );
    $geoLonRad    = deg2rad( $paramGeoLon );
    
    $minGeoLatInt = intval( round( ( $paramGeoLat * 1e6 ), 0 ) ) - $windowSize;
    $maxGeoLatInt = intval( round( ( $paramGeoLat * 1e6 ), 0 ) ) + $windowSize;
    $minGeoLonInt = intval( round( ( $paramGeoLon * 1e6 ), 0 ) ) - $windowSize;
    $maxGeoLonInt = intval( round( ( $paramGeoLon * 1e6 ), 0 ) ) + $windowSize;
    

    センターの特定の範囲内のすべての行を検索する

    SELECT
              `e`.`id`
            , :earthRadius * ACOS ( :paramGeoLatSinRad * `e`.`geoLatSinRad` + :paramGeoLatCosRad * `m`.`geoLatCosRad` * COS( `e`.`geoLonRad` - :paramGeoLonRad ) ) AS `geoDist`
    
    FROM
              `example` `e`
    WHERE
            `e`.`geoLatInt` BETWEEN :paramMinGeoLatInt AND :paramMaxGeoLatInt
            AND
            `e`.`geoLonInt` BETWEEN :paramMinGeoLonInt AND :paramMaxGeoLonInt
    HAVING `geoDist` < 20
    ORDER BY 
            `geoDist`
    

    フォーミュラの精度は非常に優れています(現在地とポイント間の距離に応じて、1メートル未満)

    データベーステーブルのexampleで次の値を事前に計算しました

    CREATE TABLE `example` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `geoLat` double NOT NULL DEFAULT '0',
      `geoLon` double NOT NULL DEFAULT '0',
    
      # below is precalculated with a trigger
      `geoLatInt` int(11) NOT NULL DEFAULT '0',
      `geoLonInt` int(11) NOT NULL DEFAULT '0',
      `geoLatSinRad` double NOT NULL DEFAULT '0',
      `geoLatCosRad` double NOT NULL DEFAULT '0',
      `geoLonRad` double NOT NULL DEFAULT '0',
      PRIMARY KEY (`id`),
      KEY `example_cIdx_geo` (`geoLatInt`,`geoLonInt`,`geoLatSinRad`,`geoLatCosRad`,`geoLonRad`)  
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC
    

    トリガーの例

    DELIMITER $
    CREATE TRIGGER 'example_before_insert' BEFORE INSERT ON `example` FOR EACH ROW
    BEGIN
        SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
        SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
        SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
        SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
        SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
    END$
    
    CREATE TRIGGER 'example_before_update' BEFORE UPDATE ON `example` FOR EACH ROW
    BEGIN
        IF NEW.geoLat <> OLD.geoLat OR NEW.geoLon <> OLD.geoLon
        THEN
            SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
            SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
            SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
            SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
            SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
        END IF;
    END$
    DELIMITER ;
    

    質問?それ以外の場合は楽しんでください:)




    1. 複数の列に対して相関サブクエリを使用する

    2. SQL Server(T-SQL)でユーザー定義データ型の名前を変更する

    3. 私のSQLコードのどこに問題がありますか?

    4. SQLの列名があいまいなクエリエラー