1

У меня есть 3 таблицы Person, Names и Notes. Каждый человек имеет несколько имен и имеет дополнительные примечания. У меня есть полный текстовый поиск по некоторым столбцам на именах и заметках (см. Ниже), они отлично работают, если слово, которое я ищу, находится в результирующем наборе или находится в db, это для пользовательских функций, php и psql. Проблема теперь в том, что когда слово I search отсутствует в db, запрос получает очень медленное значение в php и пользовательской функции, но все же быстро на psql. На psql оно меньше 1 с, другие - более 10 секунд.Postgresql Медленно на пользовательской функции, php, но быстро, если непосредственно вводить на psql, используя текстовый поиск с индексом gin

Таблицы:

Person | id, birthday 
Name | person_id, name, fs_name 
Notes | person_id, note, fs_note 

Кроме PK и FK индекс, индекс Gin на fs_name и fs_note.

Функция/Query

create or replace function queryNameFunc (TEXT) 
returns TABLE(id int, name TEXT) as $$ 

    select id, name 
    from person_name pnr 
    inner join person pr on (pnr.person_id=pr.id) 
    left join personal_notes psr on (psr.person_id = pr.id) 
    where pr.id in 
     (select distinct(id) 
     from person_name pn 
     inner join person p on (p.id = pn.person_id) 
     left join personal_notes ps on (ps.person_id = p.id) 
     where tname @@ to_tsquery($1) 
     limit 20); 

$$ language SQL; 

где условие урезана здесь, так, например, если я 'JOHN & Пробок на $ 1, и данные на БД, то результаты быстро, но если «john and james» не находятся в db, то он медленный. Это стало медленнее, поскольку у меня есть записи 1M на человека и 3M + по именам (все фиктивные записи). Любая идея о том, как это исправить? Я попытался перезапустить сервер, перезапустив postgresql.

ответ

3

База данных должна предварительно подготовить внутренний запрос, прежде чем он узнает о параметре. Это может привести к плохим запросам. Чтобы избежать этой проблемы в функции, используйте plpgsql-язык и использовать EXECUTE внутри функции:

CREATE OR REPLACE FUNCTION queryNameFunc (TEXT) RETURNS TABLE(id INT, name TEXT) AS $$ 
BEGIN 
    RETURN QUERY EXECUTE ' 
     SELECT 
      id, 
      name 
     FROM 
      person_name pnr 
       INNER JOIN person pr ON (pnr.person_id=pr.id) 
       LEFT JOIN personal_notes psr ON (psr.person_id = pr.id) 
       WHERE 
      pr.id IN(
       SELECT 
        DISTINCT(id) 
       FROM 
        person_name pn 
         INNER JOIN person p ON (p.id = pn.person_id) 
         LEFT JOIN personal_notes ps ON (ps.person_id = p.id) 
         WHERE tname @@ to_tsquery($1) 
         LIMIT 20)' USING $1; 
END; 
$$ LANGUAGE plpgsql; 

Это работает в версии 8.4, и вы должны установить plpgsql:

CREATE LANGUAGE plpgsql; 
+0

большой, его рабочий большой вопрос, какая часть ИСПОЛЬЗУЕТ 1 доллар, означает ли это, если у меня есть несколько аргументов, которые я бы сделал, ИСПОЛЬЗУЯ 1, 2, 3? И если у меня есть несколько аргументов, лучше ли это делать на if-else или где условие является частью аргумента? ($ 1 == "tname @@ ..") Большое спасибо – monmonja

+0

Да, ИСПОЛЬЗУЙТЕ $ 1, $ 2, $ 3, как вы используете несколько параметров. ИСПОЛЬЗОВАНИЕ избегает SQLinjection внутри функции, поэтому вам нужно это, когда у вас есть динамические запросы (EXECUTE). –