Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Geo-Search (Jarak) di PHP/MySQL (Kinerja)

Hitung kotak pembatas untuk memilih subset baris dalam klausa WHERE dari kueri SQL Anda, sehingga Anda hanya menjalankan penghitungan jarak yang mahal pada subset baris tersebut, bukan terhadap seluruh 200 ribu record di tabel Anda. Metode ini dijelaskan dalam artikel tentang Jenis Bergerak ini (dengan contoh kode PHP). Kemudian Anda dapat menyertakan perhitungan Haversine dalam kueri Anda terhadap subset tersebut untuk menghitung jarak sebenarnya, dan faktor dalam klausa HAVING pada titik tersebut.

Ini adalah kotak pembatas yang membantu kinerja Anda, karena itu berarti Anda hanya melakukan penghitungan jarak yang mahal pada sebagian kecil data Anda. Ini secara efektif merupakan metode yang sama yang disarankan Patrick, tetapi tautan Movable Type memiliki penjelasan ekstensif tentang metode ini, serta kode PHP yang dapat Anda gunakan untuk membuat kotak pembatas dan kueri SQL Anda.

EDIT

Jika menurut Anda haversine tidak cukup akurat, maka ada juga rumus Vincenty.

//  Vincenty formula to calculate great circle distance between 2 locations expressed as Lat/Long in KM

function VincentyDistance($lat1,$lat2,$lon1,$lon2){
    $a = 6378137 - 21 * sin($lat1);
    $b = 6356752.3142;
    $f = 1/298.257223563;

    $p1_lat = $lat1/57.29577951;
    $p2_lat = $lat2/57.29577951;
    $p1_lon = $lon1/57.29577951;
    $p2_lon = $lon2/57.29577951;

    $L = $p2_lon - $p1_lon;

    $U1 = atan((1-$f) * tan($p1_lat));
    $U2 = atan((1-$f) * tan($p2_lat));

    $sinU1 = sin($U1);
    $cosU1 = cos($U1);
    $sinU2 = sin($U2);
    $cosU2 = cos($U2);

    $lambda = $L;
    $lambdaP = 2*M_PI;
    $iterLimit = 20;

    while(abs($lambda-$lambdaP) > 1e-12 && $iterLimit>0) {
        $sinLambda = sin($lambda);
        $cosLambda = cos($lambda);
        $sinSigma = sqrt(($cosU2*$sinLambda) * ($cosU2*$sinLambda) + ($cosU1*$sinU2-$sinU1*$cosU2*$cosLambda) * ($cosU1*$sinU2-$sinU1*$cosU2*$cosLambda));

        //if ($sinSigma==0){return 0;}  // co-incident points
        $cosSigma = $sinU1*$sinU2 + $cosU1*$cosU2*$cosLambda;
        $sigma = atan2($sinSigma, $cosSigma);
        $alpha = asin($cosU1 * $cosU2 * $sinLambda / $sinSigma);
        $cosSqAlpha = cos($alpha) * cos($alpha);
        $cos2SigmaM = $cosSigma - 2*$sinU1*$sinU2/$cosSqAlpha;
        $C = $f/16*$cosSqAlpha*(4+$f*(4-3*$cosSqAlpha));
        $lambdaP = $lambda;
        $lambda = $L + (1-$C) * $f * sin($alpha) * ($sigma + $C*$sinSigma*($cos2SigmaM+$C*$cosSigma*(-1+2*$cos2SigmaM*$cos2SigmaM)));
    }

    $uSq = $cosSqAlpha*($a*$a-$b*$b)/($b*$b);
    $A = 1 + $uSq/16384*(4096+$uSq*(-768+$uSq*(320-175*$uSq)));
    $B = $uSq/1024 * (256+$uSq*(-128+$uSq*(74-47*$uSq)));

    $deltaSigma = $B*$sinSigma*($cos2SigmaM+$B/4*($cosSigma*(-1+2*$cos2SigmaM*$cos2SigmaM)- $B/6*$cos2SigmaM*(-3+4*$sinSigma*$sinSigma)*(-3+4*$cos2SigmaM*$cos2SigmaM)));

    $s = $b*$A*($sigma-$deltaSigma);
    return $s/1000;
}


echo VincentyDistance($lat1,$lat2,$lon1,$lon2);


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Hitung jumlah nilai unik

  2. Alternatif untuk mysql_real_escape_string tanpa terhubung ke DB

  3. Bagaimana Fungsi MAKE_SET() Bekerja di MySQL

  4. BATAS kata kunci di MySQL dengan pernyataan yang disiapkan

  5. Kesalahan MySQL #1071 - Kunci yang ditentukan terlalu panjang; panjang kunci maksimal adalah 767 byte