根据经纬度查找用户
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 数据
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
| USERID |
|--------|
| 101 |
我想查找 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 数据
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
| USERID |
|--------|
| 101 |