The following optimizations can be made to maximize performance of the GPS Track contact tracing API. 1) Partition very large tables by time. Contact tracing will work with non-partitioned tables too. 2) Use Oracle attribute clustering (on capture_time, longitude, latitude) to cluster tracking data on disk. This really helps, and is easy to do by altering the table after table creation, before loading any data. Also, for optimal query performance, do not compress data. To use Oracle attribute clustering, store the longitude and latitude as ordinary NUMBER columns instead of SDO_GEOMETRY. Then create a spatial function based index (demonstrated in step 4 below). -- -- Weekly interval partition example -- CREATE TABLE track_data (user_id NUMBER, longitude NUMBER, latitude NUMBER, capture_time DATE, (or TIMESTAMP) date_as_number NUMBER) PARTITION BY RANGE (capture_time) INTERVAL (NUMTODSINTERVAL(7, 'DAY')) (PARTITION "P_FIRST" VALUES LESS THAN (TIMESTAMP '2015-01-01 00:00:00') SEGMENT CREATION DEFERRED TABLESPACE users) NOLOGGING NOCOMPRESS CLUSTERING BY INTERLEAVED ORDER (capture_time,longitude,latitude); 3) Populate data_as_number column. Date arithmetic is a nice Oracle feature. It allows users to add and subtract dates. For example, SELECT to_date ('01-01-2020', 'MM-DD-YYYY') - to_date ('01-01-2019', 'MM-DD-YYYY') FROM dual; Result is 365 (days). But Date arithmetic is about 3x slower than number arithmetic. Since GPS Track contact tracing performs many date arithmetic computations, a 3x performance gain can be obtained by storing capture_time in an alternative format, NUMBER. date_as_number contains the number of days from each gps capture_time to some relative date prior to the start of all data collected. For example, if data collection started after January 1, 2019, the following UPDATE computes the number of days since January 1, 2019 for each gps position collected. UPDATE track_data SET date_as_number = (capture_time – to_date('01-01-2019', 'MM-DD-YYYY')); If possible, populate dan_as_number on INSERT instead of performing and UPDATE. By populating the date_as_number column, the API will automatically perform NUMBER arithmetic instead of date arithmetic when it searches for contacts. -- -- **NOTE** Bulk load intial set of data before creating spatial index and other indexes. -- 4) Create spatial index. Leverage Oracle native spatial indexing for scalable spatial searches. This example creates a spatial function based index. -- Create function that will be used by spatial function based index CREATE OR REPLACE FUNCTION scott.get_geom(x NUMBER, y NUMBER) RETURN SDO_GEOMETRY DETERMINISTIC parallel_enable AS begin IF x IS NULL OR y IS NULL THEN return null; ELSE return sdo_geometry(2001,4326,sdo_point_type(x,y,null),null,null); END IF; END; / -- Populate metadata to create spatial index DELETE FROM user_sdo_geom_metadata WHERE table_name='AIS_2015_MONTHLY_ATTCL'; INSERT INTO user_sdo_geom_metadata VALUES ('TRACK_DATA','SCOTT.GET_GEOM(LONGITUDE,LATITUDE)', sdo_dim_array(sdo_dim_element('x',-180,180,.05), sdo_dim_element('y',-90,90,.05)),4326); -- Create spatial index. Add LOCAL if table is partitioned. CREATE INDEX track_data_sidx ON track_data(get_geom(longitude,latitude)) indextype is mdsys.spatial_index_v2 parameters ('layer_gtype=point') PARALLEL 16 LOCAL; -- Create a view that will be used for GPS Track contact tracing CREATE OR REPLACE VIEW track_data_view AS SELECT user_id, get_geom(longitude,latitude) AS geom, capture_time, date_as_number FROM track_data; 4) Create ordinary indexes (local if table is partitioned by time) CREATE INDEX user_id_idx ON track_data (user_id) LOCAL; CREATE INDEX time_idx ON track_data (capture_time) LOCAL;