2014-11-03 1 views
1

У меня есть следующие функции, которые я хотел бы использовать в запросе 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 не могут быть оптимизированы, как ожидалось. Действительно, вопрос очень медленно по сравнению со следующим запросом (встраивание в СУЩЕСТВУЕТ-пункты без 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, логин), чтобы сгруппировать некоторые правила доступа в и использовать его в разных запросах. Я имею в виду, что можно сделать что-то вроде этого, чтобы получить хорошую производительность:

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$ 

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

Спасибо!

+0

СУЩЕСТВУЕТ имеет тенденцию приводить к коррелированных подзапросов, которые труднее СУБД для оптимизации; использование IN/NOT IN имеет тенденцию быть более эффективным. – okaram

+0

@okaram: совершенно неправильно. Обратите внимание, что в этом случае нет коррелированного подзапроса, так как основного запроса нет. – wildplasser

+0

@wildplasser, запрос в функции не коррелирован, но один выше (но я не понял, что проблема была функцией, когда я сделал комментарий :) – okaram

ответ

0

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

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

+1

Нет причин предпочитать 'NOT IN (.. .) 'здесь; 'NOT EXISTS (...)' работает так же хорошо, а может быть, даже лучше. Проблема производительности в OQ заключается в вызове функции, который не может быть разбит оптимизатором. – wildplasser

+0

@wildplasser Теперь я понимаю, что проблема заключается в вызове функции; однако, обычно в/не в запросах легче оптимизировать, не так ли? EXISTS имеют тенденцию выполнять коррелированные подзапросы (нужно ссылаться на внешнюю строку внутри запроса), которые сложнее для оптимизатора. – okaram

+1

Практически во всех моих многочисленных тестах в Postgres за последние годы «NOT EXISTS» оказался быстрее, чем 'NOT EXISTS' в'. Кроме того, 'NOT IN' демонстрирует сложное поведение со значениями NULL. [Подробнее здесь.] (Http://stackoverflow.com/a/19364694/939860). Там могут быть изменения в предстоящем п. 9.4. Не сказать, что есть, не ожидая. Пока что не проверил. –