【问题标题】:Find User on Basis of Lat long根据经纬度查找用户
【发布时间】:2015-10-21 10:16:24
【问题描述】:

我想查找位置在 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;

上面的查询占用了很多时间,请帮我找到这个场景下最快的查询。

【问题讨论】:

  • 使用 sql 索引更快地处理查询..
  • 现在写它需要 11.00 秒后索引它需要 10.00 秒没有边际差异
  • 在索引之前需要 11.000 秒,在 tagid 和 userid 上应用索引之后需要 10.000 秒。我希望这个查询在 1-2 秒内执行
  • 你在两张桌子上都应用了它吗..
  • 在UserTable中的userid是主要字段,并在lat long上应用了索引

标签: mysql sql oracle query-performance


【解决方案1】:

使用 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 |

【讨论】:

  • 嘿,我使用了 geolatlong 库并将我的 lat long 转换为一个单独的 coloum,现在它的响应时间为 1.0 秒,这个代码或 geolatlong 会更快吗?
  • 永远不要尝试 geolatlong 库,因此最好对两者进行分析(包括确保拥有适当的索引),然后您可以确定哪个更适合您的数据。但我无法回答哪个更快,因为有很多变量(硬件、分区、索引、数据量等)会影响任何答案。
  • 谢谢 MT0,我会在我实施相同的时候通知你
【解决方案2】:

如果索引没有产生很大的不同,我可能会尝试减少一些繁重的计算步骤,看看如何提高效率:

想法 1:删除ORDER BY 子句,改为在 PHP 中排序。

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

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

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

【讨论】:

  • 想法 1 和 2 无济于事。所有繁重的工作都发生在此之前。
猜你喜欢
  • 2012-09-07
  • 1970-01-01
  • 2013-05-25
  • 2012-08-14
  • 1970-01-01
  • 2021-06-28
  • 2016-05-08
  • 2021-01-17
  • 1970-01-01
相关资源
最近更新 更多