Search code examples
sqloracledatabase-performance

What is the best way to optimize a query that returns only three records that match my criteria?


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;

Solution

  • 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.

    fiddle