Невозможно выполнить функцию в plpgsql/postgres

Я хочу рассчитать расстояние от адресных точек до всех улиц на расстоянии 50 метров с помощью plpgsql. Я пробовал следующую функцию:

Create or Replace Function get_dist(ad geometry, st geometry)
Returns double precision AS
$$
Begin
Insert into street(Distance)
Select ST_Distance(ad.geom, st.geom) from ad
Left Join st ON ST_DWithin(ad.geom, st.geom, 50.0);
Return ST_Distance(ad.geom, st.geom);
End
$$
Language plpgsql volatile;

Создание функции не дает ошибки, но когда я пытаюсь вызвать ее с помощью этой команды:

Select get_dist(ad.geom, st.geom) from ad
Left Join st ON st.gid = ad.gid;

Я получаю эту ошибку:

ERROR:  missing FROM-clause entry for table "ad"
LINE 1: SELECT ST_Distance(ad.geom, st.geom)

Может кто-нибудь указать, что не так с функцией (создание функции и ее вызов)?


person khajlk    schedule 20.06.2016    source источник
comment
Пропускать целые записи - ad/st или геометрию... В функции - если ad/st это геометрия - то переписать внутренний выбор...   -  person Michał Zaborowski    schedule 20.06.2016
comment
Как вы можете выполнить эту функцию без ошибок? Есть ли в вашей базе данных таблицы ad и st (поскольку вы выбираете из них)? Пожалуйста, опишите более подробно, что вы пытаетесь выполнить.   -  person Laurenz Albe    schedule 20.06.2016


Ответы (2)


Для обработки одной строки или обработки строк одну за другой вы можете использовать предложение SQL RETURNING из INSERT в сочетании с предложением plpgsql INTO. Пример:

Но вы явно пытаетесь обработать весь набор сразу.

рассчитать расстояние от адресных точек до всех улиц на расстоянии 50 метров...

Используйте наборный подход. Намного быстрее и чище. Если вы хотите вернуть строки из INSERT, дополнительно используйте функции, возвращающие набор. Пример:

Вам вообще не нужна функция. Только этот запрос:

INSERT INTO street(ad_geom, st_geom, distance, traffic_ct)  -- any columns in street
SELECT ad.geom, st.geom, ST_Distance(ad.geom, st.geom), ad.traffic_ct
FROM   ad
LEFT   JOIN st ON ST_DWithin(ad.geom, st.geom, 50.0)
RETURNING *  -- all columns in street

Я думаю, вам больше не нужно ничего возвращать, поскольку этот запрос делает все, что вы хотели, но я оставил RETURNING в качестве доказательства концепции. Вы можете просто пропустить это.

Используйте [INNER] JOINвместо LEFT [OUTER] JOIN, если вы не хотите включать адреса без соответствующей улицы.

Руководство по RETURNING в INSERT:

Необязательное предложение RETURNING заставляет INSERT вычислять и возвращать значения на основе каждой фактически вставленной строки [...] разрешено любое выражение, использующее столбцы таблицы. Синтаксис списка RETURNING идентичен выходному списку SELECT. Будут возвращены только те строки, которые были успешно вставлены или обновлены.

Если вам нужно обернуть это в функцию plpgsql (также может быть простой функцией SQL) и при этом вернуть все строки:

CREATE OR REPLACE FUNCTION insert_neighbours()
  RETURNS SETOF street AS
$func$
BEGIN
   RETURN QUERY
   INSERT INTO street(ad_geom, st_geom, distance, traffic_ct)  -- any columns in street
   SELECT ad.geom, st.geom, ST_Distance(ad.geom, st.geom), ad.traffic_ct
   FROM   ad
   LEFT   JOIN st ON ST_DWithin(ad.geom, st.geom, 50.0)
   RETURNING *;  -- all columns in street
END
$func$  LANGUAGE plpgsql;

Вызов:

SELECT * FROM insert_neighbours();  -- use SELECT * FROM ... !

Для простоты я возвращаю всю строку, но вы также можете вернуть выбранные столбцы. См. пример выше.


Создание функции не дает ошибки

Это связано с тем, что в настоящее время PL/pgSQL выполняет только поверхностные проверки синтаксиса CREATE FUNCTION. Вы должны фактически выполнить функцию, чтобы протестировать ее, и убедиться, что все ветки кода в функциях plpgsql проверены.

person Erwin Brandstetter    schedule 21.06.2016

Вам похоже нужно рассчитать расстояние между двумя геометриями, а затем вставить это расстояние в таблицу, если оно больше 50,0. Функция будет такой:

CREATE FUNCTION get_dist(ad geometry, st geometry) RETURNS double precision AS $$
DECLARE
    dist double precision;
BEGIN
    dist := ST_Distance(ad, st);
    IF dist < 50.0 THEN
        INSERT INTO street(Distance) VALUES (dist);
    END IF;
    RETURN dist;
END;
$$ LANGUAGE plpgsql;

Однако я сомневаюсь, что вы действительно этого хотите. Во-первых, вставленная строка в таблице street будет назначена distance = dist при вызове функции и выполнении условия, но никакие другие свойства (кроме любых значений по умолчанию). Чего вы на самом деле хотите, неясно из вашего вопроса, но я надеюсь, что вы сможете работать с приведенным выше кодом, чтобы создать работающую функцию.

person Patrick    schedule 20.06.2016
comment
Спасибо. Это действительно решило мою проблему и именно то, что я хотел. Если я также хочу вернуть геометрию выбранных улиц, которые находятся в 50 метрах, то можно ли это сделать, изменив вышеуказанную функцию, или мне нужно написать отдельную функцию? - person khajlk; 20.06.2016
comment
Проблема здесь в том, что вы ничего не выбираете из таблицы street. Или st такое же, как street? Какие еще данные вы хотите хранить в таблице street? - person Patrick; 20.06.2016
comment
Я хотел сохранить другие данные, такие как геометрия выбранных улиц (geom), расстояние между адресом до выбранных улиц (помощь вам выше) и количество трафика w.r.t. адресная таблица. Должен ли я писать отдельную функцию для каждого из них? Да, ST — это таблица улиц, а ad — таблица адресов. - person khajlk; 20.06.2016