根据经纬度查找用户

Find User on Basis of Lat long

我想查找 5 英里以内且具有与我相同标签的用户。

我的数据结构:

UserTable

--------------------------------------
 userid | name | lat | long | address
--------------------------------------
  101   |  xyz |  92 |  72  | NY
  201   |  HYS |  48 |  56  | JAMAICA
  301   |  LMN |  92 |  75  | Brazil


TagTable

---------------------
 id | userid | tagid
---------------------
  1 |   101  |   5
  2 |   201  |   7
  3 |   301  |   5

查询:

SELECT vb.userid,
       vb.address,
       ( 6371 * ACOS(   COS( RADIANS( 28.684342 ) )
                          * COS( RADIANS( vb.lat ) ) 
                          * COS( RADIANS( vb.long) - RADIANS( 77.137941 ) )
                      + SIN( RADIANS( 28.684342 ) )
                          * SIN( RADIANS( vb.lat) )
                    )
       ) AS distance
  FROM UserTable vb, TagTable vk
 WHERE vk.userid = vb.userid
   AND vk.tagid = '5'
 GROUP BY vk.userid
HAVING distance < 10
 ORDER BY distance;

上面的查询很花时间,请帮我找到这个场景最快的查询。

如果索引没有太大影响,我可能会尝试去掉一些繁重的计算步骤,看看这如何提高效率:

想法 1:删除 ORDER BY 子句,改为按 PHP.

排序

想法 2:然后也删除 HAVING 子句,并在循环结果时在 PHP 中过滤。

想法3:如果userid对于TagTable中的特定tagid只能出现一次,则删除整个GROUP BY子句,并将距离测试添加到WHERE 子句。

我不确定将某些处理传递给 PHP 是否有助于提高效率,至少值得测试一下。

使用 Oracle 的 Spatial 数据

SQL Fiddle

Oracle 11g R2 架构设置:

CREATE TABLE UserTable (
  userid   NUMBER(8,0),
  name     VARCHAR2(255),
  location SDO_GEOMETRY,
  address  VARCHAR2(255)
);

INSERT INTO UserTable
          SELECT 101, 'xyz', SDO_GEOMETRY( 2001, 8307, SDO_POINT_TYPE(92,72,NULL), NULL, NULL), 'NY' FROM DUAL
UNION ALL SELECT 201, 'HYS', SDO_GEOMETRY( 2001, 8307, SDO_POINT_TYPE(48,56,NULL), NULL, NULL), 'JAMACA' FROM DUAL
UNION ALL SELECT 301, 'LMN', SDO_GEOMETRY( 2001, 8307, SDO_POINT_TYPE(92,75,NULL), NULL, NULL), 'Brazil' FROM DUAL;

INSERT INTO USER_SDO_GEOM_METADATA (
  TABLE_NAME, COLUMN_NAME, DIMINFO, SRID
) VALUES (
  'USERTABLE',
  'LOCATION', 
  SDO_DIM_ARRAY(
    SDO_DIM_ELEMENT('LONG', -180.0, 180.0, 0.5), 
    SDO_DIM_ELEMENT('LAT', -90.0, 90.0, 0.5)
  ), 
  8307
);

CREATE INDEX UserTable_SIDX ON UserTable( location ) INDEXTYPE IS MDSYS.SPATIAL_INDEX;

CREATE TABLE TagTable (id, userid, tagid ) AS
          SELECT 1, 101, 5 FROM DUAL
UNION ALL SELECT 2, 201, 7 FROM DUAL
UNION ALL SELECT 3, 301, 5 FROM DUAL;

查询 1:

SELECT u.userid
FROM   UserTable u
       INNER JOIN
       TagTable t
       ON u.UserID = t.UserID
WHERE  sdo_within_distance (
         u.location,
         SDO_GEOMETRY( 2001, 8307, SDO_POINT_TYPE(92,72,NULL), NULL, NULL),
         'distance=5 unit=MILE'
       ) = 'TRUE'
AND    t.tagid = 5

Results:

| USERID |
|--------|
|    101 |