Раздел Postgresql — Функция, вызывающая запрос Select, работает медленно

#postgresql #spring-boot #hibernate #database-partitioning

Вопрос:

Наша система основана на SAAS, мы используем идентификатор клиента в качестве маскировки для извлечения данных. Нагрузка на БД зависит от размера Компании. Поэтому мы разделили базу данных на основе идентификатора клиента

Пример: Перед Разделением

клиентская база

клиентид имя клиента адрес клиента
1 азбука
2 ЭМН
3 XYZ

штатный сотрудник

клиентид сотрудник имя сотрудника
1 123 ААА
1 124 ВВВ
2 125 ССС
2 126 DDD
3 127 EEEE

рабочий стол

клиентид джобид название работы
1 234 YTR
1 235 DER
2 236 СВЕ
3 237 VFT
3 238 GHJ

Пример: После Раздела

клиентская база

клиентид имя клиента адрес клиента
1 азбука
2 ЭМН
3 XYZ

штатный сотрудник

employeetable_1

клиентид сотрудник имя сотрудника
1 123 AAA
1 124 BBB

employeetable_2

clientid employeeid employeename
2 125 CCC
2 126 DDD

employeetable_3

clientid employeeid employeename
3 127 EEE

jobtable

jobtable_1

clientid jobid jobname
1 234 YTR
1 235 DER

jobtable_2

clientid jobid jobname
2 236 СВЕ

вакансия_3

клиентид джобид название работы
3 237 VFT
3 238 GHJ

Когда мы пишем запросы select:

Select employeeid,employeename from employeetable where clientid=2;

Этот запрос выполняется быстрее после раздела. Проблема, с которой мы сталкиваемся, заключается в том, что у нас есть определенная пользователем функция для управления некоторыми данными.

 CREATE OR REPLACE FUNCTION GET_JOB_COUNT(NUMERIC, NUMERIC) RETURNS NUMERIC AS $BODY$  DECLARE  p_client_id ALIAS FOR $1;  p_employee_id ALIAS FOR $2;  v_is_count NUMERIC := 0;  BEGIN    SELECT COUNT(JOB_ID) INTO v_is_count FROM JOBTABLE where CLIENTID=p_client_id AND CREATEDBY=p_employee_id;  RETURN v_is_count;    END; $BODY$ LANGUAGE plpgsql;  

Select employeeid,employeename,GET_JOB_COUNT(2,employeeid) from employeetable where clientid=2;

Этот запрос выполняется медленно после раздела. Означает ли это, что функция GET_JOB_COUNT выполняется через раздел?

В этом проблема, значит, мы не можем использовать подобные функции в запросе выбора после раздела?

Комментарии:

1. Функция будет вызываться один раз для каждой строки (для всех строк, выбранных с помощью clientid = 2

2. Как именно вы разделили стол? И какую версию Postgres вы используете?

3. мы используем PostgreSQL 13.4 на x86_64-pc-linux-gnu

4. Мы разделили разделы на основе «Идентификатора клиента», см. Раздел «После раздела» в вопросе

5. Каковы типы столбцов? Если вы сравниваете значения int с числовыми, это может помешать обрезке разделов.

Ответ №1:

Функция будет вызываться один раз для каждой строки таблицы employeetable (которая выбирается с помощью предложения WHERE). Я сомневаюсь, что вы сможете значительно повысить производительность, используя такой подход.

Лучше выполнить агрегацию (=подсчет) для всех строк сразу, а не для каждой строки отдельно:

 select e.employeeid, employeename, t.cnt  from employeetable e   left join (  select clientid, createdby, count(job_id) as cnt   from jobtable   group by client_id, created_by  ) j on j.clientid = e.clientid and j.createdby = e.employeeid  where e.clientid = 2;  

Другой вариант, который можно попробовать, — это использовать боковое соединение для раннего удаления строк из таблицы заданий-я не уверен, что оптимизатор достаточно умен для этого в приведенном выше запросе. Так что вы можете попробовать это в качестве альтернативы:

 select e.employeeid, employeename, j.cnt  from employeetable e   left join lateral (  select count(jt.job_id) as cnt   from jobtable jt   where jt.clientid = e.clientid  and jtcreatedby = e.employeeid  ) j on true  where e.clientid = 2;  

Если вы действительно хотите придерживаться этой функции, возможно, оптимизатору поможет сделать ее функцией SQL. Это, по крайней мере, устраняет накладные расходы на вызов кода PL/pgSQL:

 CREATE OR REPLACE FUNCTION get_job_count(p_client_id numeric, p_employee_id numeric)   returns bigint  as  $body$  SELECT COUNT(JOB_ID)   FROM JOBTABLE   where CLIENTID = p_client_id   AND CREATEDBY = p_employee_id; $BODY$ LANGUAGE sql stable parallel safe;  

Но я сомневаюсь, что вы увидите существенное улучшение от этого.

В качестве побочного нет: использование numeric для столбца «ИДЕНТИФИКАТОР» кажется довольно странным выбором. Почему вы не используете int или bigint для этого?

Комментарии:

1. Функция подсчета PLSQL, приведенная здесь, является лишь примером, у нас есть сложные манипуляции, выполняемые внутри функции, которые мы не можем решить как функцию внешнего соединения/ подзапроса/ SQL. В любом случае из вашего ответа мы делаем вывод, что вызов функции select запрос будет выполняться для каждой строки для выбранных строк, а не для каждого раздела, спасибо.

2. @RSK: если вы хотите запустить функцию для каждого раздела, вам нужно сделать ее функцией возврата набора, передающей ей ключ раздела, затем вы можете присоединиться к выходу функции