oracle 如果查询只返回三条符合条件的记录,优化该查询的最佳方法是什么?

yx2lnoni  于 2023-06-22  发布在  Oracle
关注(0)|答案(1)|浏览(158)

在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;

但是查询运行得相当慢。我只需要三个符合条件的记录。如何优化我的代码?我尝试了先取,但它不工作:

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;
n53p2ov0

n53p2ov01#

您可以将longitudelatitude存储为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

其输出:
| 名称|地址|纵向|纬度|
| - -----|- -----|- -----|- -----|
| 北京市大兴区永兴路7号院1号楼1F2F局部内装修工程 | 北京市大兴区永兴路7号院1号楼 | 116.314497 | 39.685536 |
| 北京金沃夫生物工程科技有限公司23号楼诊断试剂GMP生产车间及配套实验室、研发实验室建设项目 | 北京市大兴区华佗路50号院 | 116.282965 | 39.668402 |
您可以对GPSINFO表进行相同的更改,这样您的查询就不需要在运行时生成大量对象,并且您可以利用索引。
您也不需要同时使用空间比较和HAVERSINE函数。
fiddle

相关问题