У меня есть функция PL/pgSQL, чтобы проверить, находится ли точка в многоугольнике. Чтобы начать, я хочу сделать тест AABB по широте и долготе мин/макс, поэтому мне не нужно делать raycast. Я выполняю следующее внутри функции, чтобы получить минимумы и максимумы.Реструктуризация/Оптимизация: избегать сканирования таблицы и оператора Select медленнее в функции plsql
Моя проблема заключается в том, что каждый оператор max()/select min() занимает около 500 мс для выполнения внутри функции. Если я выполняю те же самые инструкции вне функции, запросы занимают около 20 мс каждый. Почему они настолько медленны внутри функции?
select max(latitude) into maxLat from points where location=name_input;
select max(longitude) into maxLong from points where location=name_input;
select min(latitude) into minLat from points where location=name_input;
select min(longitude) into minLong from points where location=name_input;
Вот полная функция. Как вы можете догадаться из кода, я знаю очень мало SQL, и я пишу это как для postgresql, так и для oracle (поэтому некоторые части могут быть просто плохим портом, например, иметь два массива для lat/long вместо одного массива точек, который я сделал в оракуле). Я знаю, что мой вызов очень медленный, и план показывает, что он выполняет сканирование таблицы, даже если я индексирую функцию и столбцы на ней. Мне сказали в другом вопросе, что невозможно индексировать мою функцию, потому что я передаю строку как переменную, поэтому я пытаюсь выяснить, как ее исправить.
CREATE OR REPLACE FUNCTION GEOLOCATION_CONTAINS
(
name_input IN VARCHAR, --Name of the geofilter
lat_in IN DOUBLE PRECISION, --latitude of the point to test
long_in IN DOUBLE PRECISION --longitude of the point to test
)
RETURNS INTEGER
AS $$
DECLARE
j int := 0; --index to previous point
inside int := 0; -- If the point is inside or not
numPoints int := 0; --Total number of points in the geo filter
pointsLAT DOUBLE PRECISION[]; --An array of latitudes
pointsLONG DOUBLE PRECISION[]; --An array of longitudes
maxLat double precision := 0.0;
maxLong double precision := 0.0;
minLat double precision := 0.0;
minLong double precision := 0.0;
BEGIN
--Populate the array of points by grabbing all the points in a filter
--The convention seems to be that order of a geo filter's points is defined by the order of their IDs, increasing
pointsLAT := array(SELECT latitude FROM points where location=name_input ORDER BY ID);
pointsLONG := array(SELECT longitude FROM points where location=name_input ORDER BY ID);
--Get the max/min lat/long to return before raycasting
select max(latitude) into maxLat from points where location=name_input;
select max(longitude) into maxLong from points where location=name_input;
select min(latitude) into minLat from points where location=name_input;
select min(longitude) into minLong from points where location=name_input;
--Check if it's even possible to be in the filter. If it's outside the bounds, return 0 for outside.
IF lat_in <= minLat OR lat_in >= maxLat OR long_in <= minLong OR long_in >= maxLong THEN
return 0;
END IF;
--Get the total number of points in the points array
SELECT COUNT(*) into numPoints from points where location=name_input;
--Init the pointer to the prev point index to the last guy in the array
j := numPoints;
--Perform raycast intersection test over the polgygon
for i IN 1..numPoints loop
--Test for intersection on an edge of the polygon
if((pointsLAT[i]>lat_in) != (pointsLAT[j]>lat_in)) then
if (long_in < (pointsLONG[j]-pointsLONG[i]) * (lat_in-pointsLAT[i])/(pointsLAT[j]-pointsLAT[i]) + pointsLONG[i]) then
--Intersected a line, toggle in/out
if(inside = 0) then
inside := 1;
else
inside := 0;
end if;
end if;
end if;
--set J to previous before incrementing i
j := i;
end loop;
RETURN inside;
END; $$ LANGUAGE plpgsql IMMUTABLE;
Я смотрю, чтобы найти способ получить индекс функции для работы, потому что это просто слишком медленно, если я запускаю его на стол с 200,000+ рядов (около 40 секунд теперь с оптимизациями при условии, до сих пор в ответах). Для сравнения, выполнение select *
всех объектов и запуск его через класс Polygon Java занимает 2 секунды, поэтому, очевидно, я делаю что-то не так в моей реализации plsql. В настоящее время я читаю учебники, и я вижу такие вещи, как встроенные функции и представления, чтобы ускорить процесс, но я не совсем уверен, что за чтение, чтобы ускорить его работу.
Было бы полезно узнать больше о вашей функции. Если вы, например, используете временные таблицы внутри функции, то вы технически теряете память и ресурсы диска, потому что временные таблицы кэшируются в буферах temp, которые очищаются в конце сеанса. Обычные запросы кэшируются в общих буферах и используются повторно. – JosMac