4

次のテーブル Cities があります。

ID(int),City(char),latitude(float),longitude(float).

ユーザーの経度 (例: 44.8) と緯度 (例: 46.3) に基づいて、ユーザーの近くの 100 マイル/KM 以内のすべての都市を検索したいと考えています。

いくつかの例を見つけましたが、それらを自分のケースに適応させる方法がわかりません

select *
from GEO.Cities a
where SDO_WITHIN_DISTANCE([I don`t know],
MDSYS.SDO_GEOMETRY(2001, 8307, MDSYS.SDO_POINT_TYPE(44.8,46.3, NULL) ,NULL, NULL), 
'distance = 1000') = 'TRUE';

どんな助けでも大歓迎です。

追伸:距離をとって並び替えができれば

PPS: パフォーマンスの問題により、この方法で実行したいのですが、この方法で実行しましたhttp://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQLですが、時間がかかりすぎます...

4

4 に答える 4

16

mySQL 距離検索については、かなり参考になります。

Oracle Spatial のことは忘れてください。コードが多すぎ、複雑すぎて、付加価値が十分ではありません。

トリックを実行するクエリを次に示します。これは法定マイルの距離を使用します。 編集これにより、mdarwin が言及したバグが修正されますが、北極または南極の場所に使用しようとすると分割チェックが発生します。

  SELECT id, city, LATITUDE, LONGITUDE, distance
    FROM
  (
    SELECT id, 
           city, 
           LATITUDE, LONGITUDE,
           (3959 * ACOS(COS(RADIANS(LATITUDE)) 
                 * COS(RADIANS(mylat)) 
                 * COS(RADIANS(LONGITUDE) - RADIANS(mylng)) 
                 + SIN(RADIANS(LATITUDE)) 
                 * SIN(RADIANS(mylat)) 
               ))
           AS distance,
           b.mydst
      FROM Cities
      JOIN (
        SELECT :LAT AS mylat,
               :LONG AS mylng,
               :RADIUS_LIMIT AS mydst
          FROM DUAL
      )b ON (1 = 1)
     WHERE LATITUDE >=  mylat -(mydst/69)
       AND LATITUDE <=  mylat +(mydst/69)
       AND LONGITUDE >= mylng -(mydst/(69 * COS(RADIANS(mylat))))
       AND LONGITUDE <= mylng +(mydst/(69 * COS(RADIANS(mylat))))
  )a
   WHERE distance <= mydst
   ORDER BY distance

キロメートルで作業している場合は、mydst/69 を mydst/111.045 に変更し、3959 を 6371.4 に変更します。(1/69 はマイルを度に変換します。3959 は惑星の半径の値です。)

さて、おそらくこの大きなクエリを「魔法のブラック ボックス」として使用したくなるでしょう。それをしないでください!理解するのはそれほど難しいことではありません。理解できれば、より良い仕事ができるでしょう。これが何が起こっているかです。

この句は、クエリを高速化するための心臓部です。Cities テーブルを検索して、指定した地点までの近くの都市を探します。

     WHERE LATITUDE >=  mylat -(mydst/69)
       AND LATITUDE <=  mylat +(mydst/69)
       AND LONGITUDE >= mylng -(mydst/(69 * COS(RADIANS(mylat))))
       AND LONGITUDE <= mylng +(mydst/(69 * COS(RADIANS(mylat))))

それが機能するためには、LATITUDE 列にインデックスが必要です。LONGITUDE 列のインデックスも少し役立ちます。おおよその検索を行い、ポイント近くの地表の準長方形パッチ内にある行を探します。あまりにも多くの都市を選択していますが、それほど多くはありません。

この句を使用すると、結果セットから余分な都市を削除できます。

   WHERE distance <= mydst

この句は、各都市とポイントの間の大圏距離を計算する hasersine 式です。

           (3959 * ACOS(COS(RADIANS(LATITUDE)) 
                 * COS(RADIANS(mylat)) 
                 * COS(RADIANS(LONGITUDE) - RADIANS(mylng)) 
                 + SIN(RADIANS(LATITUDE)) 
                 * SIN(RADIANS(mylat)) 

この句を使用すると、クエリにバインドされた変数として、ポイントと半径制限を 1 回だけ入力できます。さまざまな数式でこれらの変数が複数回使用されるため、これは役に立ちます。

        SELECT :LAT AS mylat,
               :LONG AS mylng,
               :RADIUS_LIMIT AS mydst
          FROM DUAL

クエリの残りの部分は単に整理するだけなので、選択して距離で並べ替えることができます。

より完全な説明は次のとおりです。 http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

于 2012-01-30T13:52:27.763 に答える
4

独自の式を作成することにした場合、この関数は Oracle ユーザーにとって非常に便利であり、他の DB 用に少し変更できると思います。これは平らな地球の公式であり、より正確なハーバーシンの公式よりも計算コストがはるかに低くなります。

CREATE OR REPLACE Function CIC3.F_FLATEARTHRAD
   ( latoriginrad IN number,
     longoriginrad IN number,
     latdestrad IN number,
     longdestrad IN number)

RETURN  number IS
   a number;
   b number;
   c number;
   u number;
   v number;

   HalfPi number:=1.5707963;
   R number:=3956;
BEGIN
   if latoriginrad is null or latdestrad is null or 
   longdestrad  is null or  longoriginrad is null then
         return null;
   end if; 
   a := HalfPi - latoriginrad;
   b := HalfPi - latdestrad;
   u := a * a + b * b;
   v := - 2 * a * b * cos(longdestrad - longoriginrad);
   c := sqrt(abs(u + v));

   return R * c;
END;

次に、クエリは次のようになります

select * from GEO.Cities a
where F_FLATEARTHRAD(44.8*0.0174,46.3*0.0174,
               latitude_radians,longitude_radians)<1000 

式は度ではなくラジアンを使用するため、0.0174 係数が必要です。したがって、ラジアンを保存する必要があります(おそらくトリガーを使用して)。または、度を受け入れるように数式を変更する必要があります。クエリの目的で、何千ものレコードをクエリしている可能性があり、1 つの余分な乗算でさえ、応答時間に違いが生じる可能性があります。私たちの場合、いくつかのクエリは、1 つのテーブルと 200k の 4k レコードの 2 つのテーブル間の距離を比較するため、数十億の関数呼び出しがあります。

以下は、時間を気にする必要がない人のための同等のハバーシンです。

CREATE OR REPLACE Function CIC3.F_HAVERSINE 
  ( latorigin IN number,
    longorigin IN number,
    latdest IN number,
    longdest IN number)

  RETURN  number IS
    v_longoriginrad number;
    v_latoriginrad number;
    v_longdestrad number;
    v_latdestrad number;
    v_difflat number;
    v_difflong number;
    a number;
    c number;
    d number;
    z number;
    x number;
    e number;
    f number;
    g number;
    h number;
    i number;
    j number;
    k number;
    l number;
    m number;
    n number;
    o number;
    p number;
    q number;
    y number;
BEGIN
    z := .017453293;
    x := 3956;
    y := 57.295780;
    v_longoriginrad:=longorigin*z;
    v_latoriginrad:=latorigin*z;
    v_longdestrad:=longdest*z;
    v_latdestrad:=latdest*z;
    v_difflong:=v_longdestrad-v_longoriginrad;
    v_difflat:=v_latdestrad-v_latoriginrad;

    j:=(v_difflat/2);
    k:=sin(j);
    l:=power(k,2);

    m:=cos(v_latoriginrad);

    n:=cos(v_latdestrad);

    o:=v_difflong/2;
    p:=sin(o);
    q:=power(p,2);

    a:=l+m*n*q;

    c := 2 * asin(sqrt(a));

    d := x * c;

    return d;
END; 
于 2012-05-18T22:52:43.680 に答える
2

本当に を使用したい場合は、Cities テーブルにSDO_WITHIN_DISTANCEタイプの列を作成し、空間インデックス メタデータを入力して、空間インデックスを作成する必要があります。SDO_GEOMETRY

  1. SDO_GEOMETRY桁:

    CREATE TABLE MYTABLE(
    ...,
    GEOLOC MDSYS.SDO_GEOMETRY,
    ...
    );
    
  2. 空間インデックス メタデータ:

    INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID)
    VALUES ('MYTABLE' /*your table name*/, 'GEOLOC', /*your spatial column name*/
        SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 1),
                  SDO_DIM_ELEMENT('Y', -90, 90, 1)),
                  8307);
    
  3. 空間インデックスの作成:

    CREATE INDEX MY_SPATIAL_IDX ON MYTABLE (GEOLOC)
    tablespace SomeTablespace; -- optional
    
  4. [わからない]と言ったところを GEOLOC に置き換えます。

これはあなたの質問に答えるものでした。他の人は、このような単純なタスクに Oracle Spatial を使用するのはやり過ぎだというヒントを与えてくれました。この場合、WHERE 句で単純なボックス化を実行して、開始点の中心と検索距離のサイズで長方形のボックスではない都市を切り取ることができるため、私は同意する傾向があります。ただし、R ツリー インデックスのインテリジェンスが必要になる場合もあります。とにかく、彼らの解決策には2つの大きな問題があります:

を。彼らは大円アプローチを使用して、ポイント間の距離を計算します。粗すぎます。より正確な結果を得るには、楕円体アプローチを使用する必要があります。グーグルはこのようにすぐに答えを提供します。

b. PL/SQL で楕円体距離アルゴリズムをプログラムすると、非常に遅くなることがわかります。解決策は、このロジックを Java または C++ に移動し、Oracle から呼び出せるようにすることです (それを行う標準的な方法があります)。

于 2012-10-08T09:35:06.850 に答える