英文:
What is the best way to optimize a query that returns only three records that match my criteria?
问题
在Oracle中,我有一个表空间,记录了许多汽车的位置。每个记录都有里程、时间、经度和纬度。表空间有五列:'PLATENO'、'SENDTIME'、'MILEAGE'以及'LONGITUDE'和'LATITUDE'。它们分别表示车辆的车牌号码、位置发送时间、里程和车辆的两个坐标。'SENDTIME'列中的值采用日期时间格式。此外,我还有一些建筑物的坐标。我想找出轨迹在距离建筑物200米以内的车辆。因此,我创建了一个名为"BUILDINGS"的表来记录建筑物,类似于以下内容:
CREATE TABLE BUILDINGS (
NAME VARCHAR2(50) NOT NULL,
ADDR VARCHAR2(50) NOT NULL,
LONGITUDE NUMBER NOT NULL,
LATITUDE NUMBER NOT NULL
);
INSERT INTO BUILDINGS (NAME, ADDR, LONGITUDE, LATITUDE) VALUES (
'北京市大兴区永兴路7号院1号楼1F2F局部内装修工程', '北京市大兴区永兴路7号院1号楼',
116.314497, 39.685536);
INSERT INTO BUILDINGS (NAME, ADDR, LONGITUDE, LATITUDE) VALUES (
'北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目', '北京市大兴区华佗路50号院',
116.282965, 39.668402);
然后,我使用"SDO_GEOM.SDO_DISTANCE"函数来计算距离,以找到符合我的要求的记录。
SELECT g.PLATENO, g.SENDTIME, g.MILEAGE, g.LONGITUDE, g.LATITUDE,
b.NAME AS BUILDING_NAME, b.ADDR, b.LONGITUDE, b.LATITUDE
FROM GPSINFO g, BUILDINGS b
WHERE SDO_GEOM.SDO_DISTANCE (
SDO_GEOMETRY (2001, 4326, SDO_POINT_TYPE (g.LONGITUDE, g.LATITUDE, NULL), NULL, NULL),
SDO_GEOMETRY (2001, 4326, SDO_POINT_TYPE (b.LONGITUDE, b.LATITUDE, NULL), NULL, NULL),
0.01,
'unit=M'
) <= 200;
但是查询运行速度较慢。我只需要符合我的条件的三条记录。如何优化我的代码?我尝试使用"FETCH FIRST",但它不起作用:
SELECT v.PLATENO, v.SENDTIME, v.MILEAGE, v.LONGITUDE, v.LATITUDE, b.NAME AS BUILDING_NAME
FROM VEHICLES v, BUILDINGS b
WHERE SDO_GEOM.SDO_DISTANCE (
SDO_GEOMETRY (2001, 4326, SDO_POINT_TYPE (v.LONGITUDE, v.LATITUDE, NULL), NULL, NULL),
SDO_GEOMETRY (2001, 4326, SDO_POINT_TYPE (b.LONGITUDE, b.LATITUDE, NULL), NULL, NULL),
0.01,
'unit=M'
) <= 50
AND HAVERSINE(v.LONGITUDE, v.LATITUDE, b.LONGITUDE, b.LATITUDE) <= 50
ORDER BY v.SENDTIME DESC
FETCH FIRST 3 ROWS ONLY;
英文:
In Oracle: I have a tablespace that records the positions of many cars. Each record has a mileage, time, LONGITUDE and LATITUDE. The tablespace has five columns: 'PLATENO' ‘SENDTIME’, ‘MILEAGE’, and 'LONGITUDE' 'LATITUDE'. They represent the plate number of cars, positon send time, mileage and two coordinates of cars respectively. The values in the ‘SENDTIME’ column are in the datetime format. ALSO, i have some coordinates of buildings. I want to find out the vehicles whose trajectory passes 200 meters near the buildings. So i create a table named buildings to record the buildings, something like this:
CREATE TABLE BUILDINGS (
NAME VARCHAR2(50) NOT NULL,
ADDR VARCHAR2(50) NOT NULL,
LONGITUDE NUMBER NOT NULL,
LATITUDE NUMBER NOT NULL
);
INSERT INTO BUILDINGS (NAME, ADDR, LONGITUDE, LATITUDE) VALUES (
'北京市大兴区永兴路7号院1号楼1F2F局部内装修工程', '北京市大兴区永兴路7号院1号楼',
116.314497, 39.685536);
INSERT INTO BUILDINGS (NAME, ADDR, LONGITUDE, LATITUDE) VALUES (
'北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目', '北京市大兴区华佗路50号院',
116.282965, 39.668402);
Then I use sdo_geom.sdo_distance function to calculate distance to find the reords to meet my requirements.
select g.PLATENO, g.SENDTIME, g.MILEAGE, g.LONGITUDE, g.LATITUDE,
b.NAME as BUILDING_NAME, b.ADDR, b. LONGITUDE, b.LATITUDE
from GPSINFO g, BUILDINGS b
where sdo_geom.sdo_distance (
sdo_geometry (2001, 4326, sdo_point_type (g.LONGITUDE, g.LATITUDE, null), null, null),
sdo_geometry (2001, 4326, sdo_point_type (b.LONGITUDE, b.LATITUDE, null), null, null),
0.01,
'unit=M'
) <= 200;
But the query runs rather slowly. I only need three records that match my criteria. How to optimize my code? i tried FETCH FIRST, but it doesn't work:
select v.PLATENO, v.SENDTIME, v.MILEAGE, v.LONGITUDE, v.LATITUDE, b.NAME as BUILDING_NAME
from VEHICLES v, BUILDINGS b
where sdo_geom.sdo_distance (
sdo_geometry (2001, 4326, sdo_point_type (v.LONGITUDE, v.LATITUDE, null), null, null),
sdo_geometry (2001, 4326, sdo_point_type (b.LONGITUDE, b.LATITUDE, null), null, null),
0.01,
'unit=M'
) <= 50
and HAVERSINE(v.LONGITUDE, v.LATITUDE, b.LONGITUDE, b.LATITUDE) <= 50
ORDER BY v.SENDTIME DESC
FETCH FIRST 3 ROWS ONLY;
答案1
得分: 4
不必将longitude
和latitude
存储为NUMBER
数据类型,然后在查询中转换为SDO_GEOMETRY
,您可以将它们存储为SDO_GEOMETRY
类型并添加空间索引:
CREATE TABLE BUILDINGS (
NAME VARCHAR2(200) NOT NULL,
ADDR VARCHAR2(200) NOT NULL,
LOCATION SDO_GEOMETRY NOT NULL
);
INSERT INTO USER_SDO_GEOM_METADATA (
TABLE_NAME, COLUMN_NAME, DIMINFO, SRID
) VALUES (
'BUILDINGS',
'LOCATION',
SDO_DIM_ARRAY(
SDO_DIM_ELEMENT('LONG', -180.0, 180.0, 0.0001),
SDO_DIM_ELEMENT('LAT', -90.0, 90.0, 0.0001)
),
4326
);
CREATE INDEX Buildings_SIDX ON Buildings( location ) INDEXTYPE IS MDSYS.SPATIAL_INDEX;
然后,您可以插入数据:
INSERT INTO BUILDINGS (NAME, ADDR, LOCATION)
VALUES (
'北京市大兴区永兴路7号院1号楼1F2F局部内装修工程',
'北京市大兴区永兴路7号院1号楼',
sdo_geometry(2001, 4326, sdo_point_type (116.314497, 39.685536, null), null, null)
);
INSERT INTO BUILDINGS (NAME, ADDR, LOCATION)
VALUES (
'北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目',
'北京市大兴区华佗路50号院',
sdo_geometry(2001, 4326, sdo_point_type (116.282965, 39.668402, null), null, null)
);
如果您想获取位置的各个组件,可以使用嵌套对象(注意:您需要确保从表别名开始):
SELECT b.name,
b.addr,
b.location.sdo_point.x AS longitude,
b.location.sdo_point.y AS latitude
FROM buildings b
输出如下:
NAME | ADDR | LONGITUDE | LATITUDE |
---|---|---|---|
北京市大兴区永兴路7号院1号楼1F2F局部内装修工程 | 北京市大兴区永兴路7号院1号楼 | 116.314497 | 39.685536 |
北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目 | 北京市大兴区华佗路50号院 | 116.282965 | 39.668402 |
您可以对GPSINFO
表进行相同的更改,然后您的查询将不需要在运行时生成大量对象,并且可以利用索引。
您还不需要同时使用空间比较和HAVERSINE
函数。
英文:
Rather than storing the longitude
and latitude
as NUMBER
data types and then converting to SDO_GEOMETRY
in the query, you can store them as SDO_GEOMETRY
types and add a spatial index:
CREATE TABLE BUILDINGS (
NAME VARCHAR2(200) NOT NULL,
ADDR VARCHAR2(200) NOT NULL,
LOCATION SDO_GEOMETRY NOT NULL
);
INSERT INTO USER_SDO_GEOM_METADATA (
TABLE_NAME, COLUMN_NAME, DIMINFO, SRID
) VALUES (
'BUILDINGS',
'LOCATION',
SDO_DIM_ARRAY(
SDO_DIM_ELEMENT('LONG', -180.0, 180.0, 0.0001),
SDO_DIM_ELEMENT('LAT', -90.0, 90.0, 0.0001)
),
4326
);
CREATE INDEX Buildings_SIDX ON Buildings( location ) INDEXTYPE IS MDSYS.SPATIAL_INDEX;
Then you can insert the data:
INSERT INTO BUILDINGS (NAME, ADDR, LOCATION)
VALUES (
'北京市大兴区永兴路7号院1号楼1F2F局部内装修工程',
'北京市大兴区永兴路7号院1号楼',
sdo_geometry(2001, 4326, sdo_point_type (116.314497, 39.685536, null), null, null)
);
INSERT INTO BUILDINGS (NAME, ADDR, LOCATION)
VALUES (
'北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目',
'北京市大兴区华佗路50号院',
sdo_geometry(2001, 4326, sdo_point_type (116.282965, 39.668402, null), null, null)
);
If you want to get the components of the location then you can use the nested objects (note: you need to ensure you start with the table alias):
SELECT b.name,
b.addr,
b.location.sdo_point.x AS longitude,
b.location.sdo_point.y AS latitude
FROM buildings b
Which outputs:
NAME | ADDR | LONGITUDE | LATITUDE |
---|---|---|---|
北京市大兴区永兴路7号院1号楼1F2F局部内装修工程 | 北京市大兴区永兴路7号院1号楼 | 116.314497 | 39.685536 |
北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目 | 北京市大兴区华佗路50号院 | 116.282965 | 39.668402 |
You can make the same change to the GPSINFO
table and then your queries will not need to generate lots of objects at run-time and you can take advantage of the indexes.
You also do not need to use both spatial comparisons and your HAVERSINE
function.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论