Плохая производительность при выполнении EXISTS-функций в функциях

У меня есть следующая функция, которую я хотел бы использовать в sql-запросе (Postgres 9.3):

SELECT * FROM test_table tt WHERE has_access(tt.id, tt.login)

CREATE OR REPLACE FUNCTION has_access(integer, integer)
RETURNS boolean AS
$BODY$
SELECT
 EXISTS (SELECT true
 FROM test_read_access
 WHERE id = $1 and login = $2 
 )
 AND
 NOT EXISTS (SELECT true
 FROM test_no_read_access
 WHERE id = $1 and login = $2 
 )
$BODY$

Это прекрасно работает, пока я должен касаться только функциональной корректности. Поскольку анализатор запросов сообщает мне, что функция должна быть оценена для каждой строки, и поэтому предложения EXISTS не могут быть оптимизированы, как ожидалось. Действительно, запрос выполняется очень медленно по сравнению со следующим запросом (вложение предложений EXISTS без предложения SELECT):

SELECT * FROM test_table tt WHERE 
 EXISTS (SELECT true
 FROM test_read_access
 WHERE id = tt.id and login = tt.login 
 )
 AND
 NOT EXISTS (SELECT true
 FROM test_no_read_access
 WHERE id = tt.id and login = tt.login 
 )

Цель функции has_access (id, login) состоит в том, чтобы сгруппировать некоторое правило доступа в функцию и использовать ее в разных запросах. Я имею в виду, что можно сделать что-то подобное, чтобы получить хорошую производительность:

SELECT * FROM test_table tt WHERE EXISTS (select has_access(tt.id, tt.login))

CREATE OR REPLACE FUNCTION has_access(integer, integer)
RETURNS SETOF boolean AS
$BODY$
SELECT true
 FROM test_read_access
WHERE id = $1 and login = $2 
$BODY$

Но теперь у меня есть только один подзапрос в одной таблице в функции, и это не полезно в моем случае. Любое предложение о том, как сделать это правильно, чтобы не столкнуться с проблемами производительности?

Благодарю!

1 ответ

Хорошо, я думаю, я вижу, в чем твоя проблема; вызовы функций не оптимизируются, поэтому вам нужно выполнить запрос вне функции; что-то вроде

SELECT *
 FROM test_table
WHERE (id,login) IN (SELECT id,login FROM test_read_access)
 AND (id,login) NOT IN (SELECT id,login FROM test_no_read_access)

Проверьте http://sqlfiddle.com/#!12/94a02/2

licensed under cc by-sa 3.0 with attribution.