Подключим расширение pg_stat_statements, с помощью которого будем строить профиль.
=> CREATE EXTENSION pg_stat_statements;
CREATE EXTENSION
=> ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';
ALTER SYSTEM
Подключение библиотеки требует перезагрузки.
=> \q
student$ sudo pg_ctlcluster 10 main restart
student$ psql demo
Настроим расширение так, чтобы собиралась информация о всех запросах, в том числе вложенных.
=> SET pg_stat_statements.track = 'all';
SET
Создадим представление, чтобы смотреть на собранную статистику выполнения операторов:
=> CREATE VIEW statements_v AS SELECT substring(regexp_replace(query,' +',' ','g') FOR 55) AS query, calls, round(total_time)/1000 AS time_sec, shared_blks_hit + shared_blks_read + shared_blks_written AS shared_blks FROM pg_stat_statements ORDER BY total_time DESC;
CREATE VIEW
Здесь мы для простоты показываем только часть полей и выводим только общее число страниц кэша.
Задача: построить отчет, выводящий сводную таблицу количества перевезенных пассажиров: строками отчета должны быть модели самолетов, а столбцами - категории обслуживания.
Рассмотрим следующую реализацию. Сначала создадим функцию, возвращающую число пассажиров для заданной модели и категории обслуживания:
=> CREATE FUNCTION qty(aircraft_code char, fare_conditions varchar) RETURNS bigint AS $$ SELECT count(*) FROM flights f JOIN boarding_passes bp ON bp.flight_id = f.flight_id JOIN seats s ON s.aircraft_code = f.aircraft_code AND s.seat_no = bp.seat_no WHERE f.aircraft_code = qty.aircraft_code AND s.fare_conditions = qty.fare_conditions; $$ STABLE LANGUAGE sql;
CREATE FUNCTION
Для отчета создадим функцию, возвращающую набор строк:
=> CREATE FUNCTION report() RETURNS TABLE(model text, economy bigint, comfort bigint, business bigint) AS $$ DECLARE r record; BEGIN FOR r IN SELECT a.aircraft_code, a.model FROM aircrafts a ORDER BY a.model LOOP report.model := r.model; report.economy := qty(r.aircraft_code, 'Economy'); report.comfort := qty(r.aircraft_code, 'Comfort'); report.business := qty(r.aircraft_code, 'Business'); RETURN NEXT; END LOOP; END; $$ STABLE LANGUAGE plpgsql;
CREATE FUNCTION
Сбросим статистику.
=> SELECT pg_stat_statements_reset();
pg_stat_statements_reset -------------------------- (1 row)
=> SELECT * FROM report();
model | economy | comfort | business ---------------------+---------+---------+---------- Аэробус A319-100 | 337129 | 0 | 70232 Аэробус A320-200 | 0 | 0 | 0 Аэробус A321-200 | 649388 | 0 | 127982 Боинг 737-300 | 589901 | 0 | 59829 Боинг 767-300 | 817621 | 0 | 127947 Боинг 777-300 | 895896 | 132571 | 83080 Бомбардье CRJ-200 | 1155683 | 0 | 0 Сессна 208 Караван | 111096 | 0 | 0 Сухой Суперджет-100 | 2425034 | 0 | 342423 (9 rows)
Посмотрим, какую статистику мы получили:
=> SELECT * FROM statements_v \gx
-[ RECORD 1 ]-------------------------------------------------------- query | SELECT * FROM report() calls | 1 time_sec | 25.752 shared_blks | 946027 -[ RECORD 2 ]-------------------------------------------------------- query | SELECT count(*) + | FROM flights f + | JOIN boarding_passes calls | 27 time_sec | 25.726 shared_blks | 945587 -[ RECORD 3 ]-------------------------------------------------------- query | SELECT a.aircraft_code, a.model FROM aircrafts a ORDER calls | 1 time_sec | 0.002 shared_blks | 21 -[ RECORD 4 ]-------------------------------------------------------- query | SELECT pg_stat_statements_reset() calls | 1 time_sec | 0 shared_blks | 0
Сбросим статистику и попробуем вывести тот же отчет одним запросом:
=> SELECT pg_stat_statements_reset();
pg_stat_statements_reset -------------------------- (1 row)
=> WITH t AS ( SELECT f.aircraft_code, count(*) FILTER (WHERE s.fare_conditions = 'Economy') economy, count(*) FILTER (WHERE s.fare_conditions = 'Comfort') comfort, count(*) FILTER (WHERE s.fare_conditions = 'Business') business FROM flights f JOIN boarding_passes bp ON bp.flight_id = f.flight_id JOIN seats s ON s.aircraft_code = f.aircraft_code AND s.seat_no = bp.seat_no GROUP BY f.aircraft_code ) SELECT a.model, coalesce(t.economy,0) economy, coalesce(t.comfort,0) comfort, coalesce(t.business,0) business FROM aircrafts a LEFT JOIN t ON a.aircraft_code = t.aircraft_code ORDER BY a.model;
model | economy | comfort | business ---------------------+---------+---------+---------- Аэробус A319-100 | 337129 | 0 | 70232 Аэробус A320-200 | 0 | 0 | 0 Аэробус A321-200 | 649388 | 0 | 127982 Боинг 737-300 | 589901 | 0 | 59829 Боинг 767-300 | 817621 | 0 | 127947 Боинг 777-300 | 895896 | 132571 | 83080 Бомбардье CRJ-200 | 1155683 | 0 | 0 Сессна 208 Караван | 111096 | 0 | 0 Сухой Суперджет-100 | 2425034 | 0 | 342423 (9 rows)
Посмотрим статистику теперь:
=> SELECT * FROM statements_v \gx
-[ RECORD 1 ]---------------------------------- query | WITH t AS ( + | SELECT f.aircraft_code, + | count(*) FILTER calls | 1 time_sec | 8.061 shared_blks | 57086 -[ RECORD 2 ]---------------------------------- query | SELECT pg_stat_statements_reset() calls | 1 time_sec | 0 shared_blks | 0
Обратите внимание, как уменьшилось время выполнения запроса и насколько меньше страниц пришлось прочитать благодаря устранению избыточных чтений.
Можно посчитать общее количество страниц во всех задействованных таблицах:
=> SELECT sum(relpages) FROM pg_class WHERE relname IN ('flights','boarding_passes','aircrafts','seats');
sum ------- 57085 (1 row)
Это число может служить грубой оценкой сверху для запроса, которому требуются все строки: обработка существенно большего числа страниц может говорить о том, что данные перебираются по нескольку раз.
Конец демонстрации.