问题
I want to find users which are under 5 miles location and having the same tag that I have.
The structure of my data:
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
Query:
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;
The above query is taking lot of time, kindly help me to find the fastest query for this scenario.
回答1:
If indexing doesn't make a big difference I might try and take some of the heavy computational steps out, and see how that improves efficiency:
Idea 1: remove the ORDER BY
clause, and instead sort in PHP.
Idea 2: then also remove the HAVING
clause, and instead filter in PHP as you loop through the results.
Idea 3: If a userid
can only appear once for a particular tagid
in TagTable
, remove the whole GROUP BY
clause, and add the distance test into the WHERE
clause.
I'm not sure if passing off some of the processing to PHP would help efficiency, by it's worth testing at least.
回答2:
Use Oracle's Spatial data
SQL Fiddle
Oracle 11g R2 Schema Setup:
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;
Query 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 |
来源:https://stackoverflow.com/questions/33256674/find-user-on-basis-of-lat-long