我在sql中不是很好,所以我在排序方面遇到了一些问题。 我有一个表格,其中包含一些地点的地理坐标。我需要找到10个最近的位置并显示它们。所以为此,我使用了这样的SQL查询:
SELECT
6373*2*ATAN2(
POWER(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2) , 0.5),
POWER(1-(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2)),0.5)) AS d
FROM
(SELECT
(f.loc_lat)*3.1415/180 AS f_lat, (f.loc_lon)*3.1415/180 AS f_lon,
(s.loc_lat)*3.1415/180 AS s_lat, (s.loc_lon)*3.1415/180 AS s_lon,
(s.loc_lat)*3.1415/180 - (f.loc_lat)*3.1415/180 AS lat,
(s.loc_lon)*3.1415/180 - (f.loc_lon)*3.1415/180 AS lon
FROM htl f, htl s
WHERE f.htl_cd != s.htl_cd
AND f.loc_lat is not null
AND f.loc_lon is not null
AND f.ctry_cd = s.ctry_cd
) location
)order by d asc;
如果我在没有最后一行的情况下运行此查询,一切都很好。但是,当我想要排序结果时,我有问题。对我来说排序很长(接近30分钟)。
查询次数接近2800万行。
所以我可能需要重写我的查询,或者你可能知道一些提示来加快它的速度。
更新
表htl中的数据如下所示:
htl_cd loc_lat loc_lon
GDLUA 20.690688 -103.416281
LPLRL 53.463509 -2.876497
BDLWL 41.925992 -72.670218
MKCGM 38.892569 -94.523921
第一列是简单的主键。 第二和第三列是纬度和经度(坐标)
6373*2*ATAN2(
POWER(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2) , 0.5),
POWER(1-(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2)),0.5)) AS d
这个公式用于计算两个位置之间的距离
UPDATE2 对于需要执行计划的人来说
计划哈希值:446529610
| Id |操作|名称|行|字节| TempSpc |成本(%CPU)|时间 |
| 0 |选择声明| | 12M | 733M | | 186K(2)| 00:37: 22 |
| 1 |排序顺序| | 12M | 733M | 1771M | 186K(2)| 00:37: 22 |
| * 2 | HASH JOIN | | 12M | 733M | | 259(84)| 00:00: 04 |
| * 3 |表访问完全| HTL | 5081 | 148K | | 22(0)| 00:00: 01 |
| 4 |表访问完全| HTL | 5411 | 158K | | 22(0)| 00:00: 01 |
2 - access("F"."CTRY_CD"="S"."CTRY_CD")
filter("F"."HTL_CD"<>"S"."HTL_CD")
3 - filter("F"."LOC_LAT" IS NOT NULL)
537 recursive calls
18 db block gets
152 consistent gets
68534 physical reads
0 redo size
463630284 bytes sent via SQL*Net to client
9504847 bytes received via SQL*Net from client
864044 SQL*Net roundtrips to/from client
0 sorts (memory)
1 sorts (disk)
12960638 rows processed
答案 0 :(得分:2)
如果我在没有最后一行的情况下运行此查询,一切都很好。
您的意思是您是从查询中快速返回第一行,还是快速返回整组2800万行?您的查询非常耗费计算量。
但是当我想对结果进行排序时,我有问题。对我来说排序很长(接近30分钟)。
排序要求计算所有值,并且在返回任何行之前对它们进行排序。如果所有行都是从未排序的查询中快速返回的,则排序阶段需要很长时间,而可能的原因是排序正在溢出到临时磁盘。您可以在查询运行时监视v $ sql_workarea_active视图所需的排序空间,这将告诉您需要分配多少内存以避免使用临时磁盘空间。
您还可以使用gather_plan_statistics提示和DBMS_XPlan准确地分析执行计划中每个阶段的时间。 http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_xplan.htm#ARPLS70137
最后,你真的需要对所有2800万行进行排序吗?你需要知道最远的点,或者最接近的点吗?如果是后者,也许您可以考虑在ORDER BY之前过滤结果集。
答案 1 :(得分:2)
您的查询返回每个距离两次。如果您有最接近的行a
和b
,那么它会返回a
到b
和b
到a
的距离。只需比较两种排列之一的距离,就可以消除一半的计算:
Oracle 11g R2架构设置:
CREATE TABLE htl ( htl_cd, loc_lat, loc_lon, ctry_cd ) AS
SELECT 'GDLUA', 20.690688, -103.416281, 1 FROM DUAL
UNION ALL SELECT 'LPLRL', 53.463509, -2.876497, 1 FROM DUAL
UNION ALL SELECT 'BDLWL', 41.925992, -72.670218, 1 FROM DUAL
UNION ALL SELECT 'MKCGM', 38.892569, -94.523921, 1 FROM DUAL
/
BEGIN
FOR i IN 1 .. 20 LOOP
INSERT INTO htl VALUES(
'GD' || LPAD( TO_CHAR( i ), 3, '0' ),
20.690688 + i / 1000,
-103.416281 + i / 1000,
1
);
END LOOP;
END;
/
查询1 :
WITH indexed_locations AS (
SELECT h.*,
ROWNUM AS idx
FROM htl h
),
location AS (
SELECT
(f.loc_lat)*3.1415/180 AS f_lat, (f.loc_lon)*3.1415/180 AS f_lon,
(s.loc_lat)*3.1415/180 AS s_lat, (s.loc_lon)*3.1415/180 AS s_lon,
(s.loc_lat)*3.1415/180 - (f.loc_lat)*3.1415/180 AS lat,
(s.loc_lon)*3.1415/180 - (f.loc_lon)*3.1415/180 AS lon
FROM indexed_locations f
INNER JOIN
indexed_locations s
ON (
f.htl_cd != s.htl_cd
AND f.ctry_cd = s.ctry_cd
AND f.idx < s.idx
)
WHERE
f.loc_lat is not null
AND f.loc_lon is not null
),
distances AS (
SELECT
6373*2*ATAN2(
POWER(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2) , 0.5),
POWER(1-(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2)),0.5)) AS d
FROM location
ORDER BY d ASC
)
SELECT d
FROM distances
WHERE ROWNUM < 11
<强> Results 强>:
| D |
|----------------|
| 0.152300994984 |
| 0.152301463917 |
| 0.152301932829 |
| 0.152302401722 |
| 0.152302870595 |
| 0.152303339447 |
| 0.15230380828 |
| 0.152304277093 |
| 0.152304745885 |
| 0.152305214658 |
您还可以将所有计算移动到用户定义的函数中并生成函数DETERMINISTIC
(如果使用相同的输入参数将有所帮助) - 但是,请注意在分析它时运行一次然后可以缓存结果,后续运行可以使用缓存的结果,这样您每次都不会看到查询的真实成本:
CREATE OR REPLACE FUNCTION calc_Lat_Long_Distance(
f_loc_lat HTL.LOC_LAT%TYPE,
f_loc_lon HTL.LOC_LON%TYPE,
s_loc_lat HTL.LOC_LAT%TYPE,
s_loc_lon HTL.LOC_LON%TYPE
) RETURN NUMBER DETERMINISTIC
AS
PI CONSTANT REAL := 3.1415/180;
f_lat REAL := f_loc_lat * PI;
f_lon REAL := f_loc_lon * PI;
s_lat REAL := s_loc_lat * PI;
s_lon REAL := s_loc_lon * PI;
lat_dif REAL := s_lat - f_lat;
lon_dif REAL := s_lon - f_lon;
p REAL := POWER(SIN(lat_dif/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon_dif/2),2);
BEGIN
RETURN 6373*2*ATAN2( POWER(p, 0.5), POWER(1-p,0.5) );
END;
/
您也可以使用RESULT_CACHE
代替DETERMINISTIC
- 对其进行分析,看看是否会产生特殊差异。
查询2 :
WITH indexed_locations AS (
SELECT h.*,
ROWNUM AS idx
FROM htl h
),
distances AS (
SELECT calc_Lat_Long_Distance(
f.loc_lat,
f.loc_lon,
s.loc_lat,
s.loc_lon
) AS d
FROM indexed_locations f
INNER JOIN
indexed_locations s
ON (
f.htl_cd != s.htl_cd
AND f.ctry_cd = s.ctry_cd
AND f.idx < s.idx
)
WHERE
f.loc_lat is not null
AND f.loc_lon is not null
ORDER BY d ASC
)
SELECT d
FROM distances
WHERE ROWNUM < 11
<强> Results 强>:
| D |
|----------------|
| 0.152300994984 |
| 0.152301463917 |
| 0.152301932829 |
| 0.152302401722 |
| 0.152302870595 |
| 0.152303339447 |
| 0.15230380828 |
| 0.152304277093 |
| 0.152304745885 |
| 0.152305214658 |
答案 2 :(得分:0)
SELECT * FROM (
SELECT
6373*2*ATAN2(
POWER(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2) , 0.5),
POWER(1-(POWER(SIN(lat/2),2) + COS(f_lat)*COS(s_lat)*POWER(SIN(lon/2),2)),0.5)) AS d,
ROW_NUMBER() OVER (ORDER BY d asc) AS rownum
FROM
(SELECT
(f.loc_lat)*3.1415/180 AS f_lat, (f.loc_lon)*3.1415/180 AS f_lon,
(s.loc_lat)*3.1415/180 AS s_lat, (s.loc_lon)*3.1415/180 AS s_lon,
(s.loc_lat)*3.1415/180 - (f.loc_lat)*3.1415/180 AS lat,
(s.loc_lon)*3.1415/180 - (f.loc_lon)*3.1415/180 AS lon
FROM htl f, htl s
WHERE f.htl_cd != s.htl_cd
AND f.loc_lat is not null
AND f.loc_lat is not null
AND f.ctry_cd = s.ctry_cd
) location
)WHERE rownum <= 10;
如果您只需要前10条记录,请尝试以上。