Этапы выполнения запроса

Установим параметры, которые покажут примерное время выполнения этапов обработки:

=> ALTER SYSTEM SET log_parser_stats = on;
ALTER SYSTEM
=> ALTER SYSTEM SET log_planner_stats = on;
ALTER SYSTEM
=> ALTER SYSTEM SET log_executor_stats = on;
ALTER SYSTEM
=> SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)


Обычный удобный способ узнать время выполнения и время планирования (включая разбор и переписывание) - команда EXPLAIN ANALYZE. Ее основное назначение - показать план выполнения запроса, но о плане как таковом мы будем говорить позже. Пока обратите внимание на две последние строки:

=> EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT * FROM ticket_flights;
                        QUERY PLAN                        
----------------------------------------------------------
 Seq Scan on ticket_flights (actual rows=8391852 loops=1)
 Planning time: 4.235 ms
 Execution time: 1993.501 ms
(3 rows)


Благодаря установленным нами параметрам, из журнала сообщений можно получить более подробную информацию, хотя обычно это и не требуется. Выведем только основные цифры (elapsed):

postgres$ tail -n 50 /var/log/postgresql/postgresql-10-main.log | egrep 'LOG: |elapsed'
2019-02-13 13:02:46.387 MSK [3054] postgres@demo LOG:  PARSER STATISTICS
	!	0.000000 s user, 0.000000 s system, 0.000029 s elapsed
2019-02-13 13:02:46.392 MSK [3054] postgres@demo LOG:  PARSE ANALYSIS STATISTICS
	!	0.000000 s user, 0.000000 s system, 0.005306 s elapsed
2019-02-13 13:02:46.392 MSK [3054] postgres@demo LOG:  REWRITER STATISTICS
	!	0.000000 s user, 0.000000 s system, 0.000001 s elapsed
2019-02-13 13:02:46.397 MSK [3054] postgres@demo LOG:  PLANNER STATISTICS
	!	0.000000 s user, 0.004000 s system, 0.004176 s elapsed
2019-02-13 13:02:48.391 MSK [3054] postgres@demo LOG:  EXECUTOR STATISTICS
	!	0.536000 s user, 0.616000 s system, 1.997833 s elapsed

Разные способы измерения выдают несколько отличающиеся результаты.


Простой запрос

Протокол простых запросов применяется, когда на сервер просто отправляется оператор (и, если это SELECT или команда с фразой RETURNING, то мы ожидаем получение всех строк результата). Например:

=> SELECT model FROM aircrafts WHERE aircraft_code = '773';
     model     
---------------
 Боинг 777-300
(1 row)


Подготовленные операторы

Создадим подготовленный оператор для нашего запроса:

=> PREPARE model(varchar) AS
  SELECT model FROM aircrafts WHERE aircraft_code = $1;
PREPARE

Теперь мы можем вызывать оператор по имени:

=> EXECUTE model('773');
     model     
---------------
 Боинг 777-300
(1 row)

=> EXECUTE model('763');
     model     
---------------
 Боинг 767-300
(1 row)


Все подготовленные операторы можно увидеть в представлении:

=> SELECT * FROM pg_prepared_statements \gx
-[ RECORD 1 ]---+--------------------------------------------------------
name            | model
statement       | PREPARE model(varchar) AS                              +
                |   SELECT model FROM aircrafts WHERE aircraft_code = $1;
prepare_time    | 2019-02-13 13:02:48.603462+03
parameter_types | {"character varying"}
from_sql        | t


Если подготовленный оператор больше не нужен, его можно удалить командой DEALLOCATE, но в любом случае оператор пропадет при завершении сеанса.

=> \c
You are now connected to database "demo" as user "postgres".
=> SELECT * FROM pg_prepared_statements;
 name | statement | prepare_time | parameter_types | from_sql 
------+-----------+--------------+-----------------+----------
(0 rows)

Команды PREPARE, EXECUTE, DEALLOCATE - команды SQL. Клиенты на других языках программирования будут использовать операции, определенные в соответствующем драйвере. Но любой драйвер использует один и тот же протокол для взаимодействия с сервером.


Курсоры

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

На SQL использование курсоров можно продемонстрировать следующим образом. Объявляем (и сразу же открываем) курсор и выбираем первую строку:

=> BEGIN;
BEGIN
=> DECLARE c CURSOR FOR SELECT * FROM aircrafts;
DECLARE CURSOR
=> FETCH c;
 aircraft_code |     model     | range 
---------------+---------------+-------
 773           | Боинг 777-300 | 11100
(1 row)


Читаем вторую строку результата и закрываем открытый курсор.

=> FETCH c;
 aircraft_code |     model     | range 
---------------+---------------+-------
 763           | Боинг 767-300 |  7900
(1 row)

=> CLOSE c;
CLOSE CURSOR
=> COMMIT;
COMMIT

Конец демонстрации.