Представления и материализованные представления
Представление — это именованный SQL-запрос, хранящийся в базе данных.
Представления позволяют сохранять часто используемые или сложные запросы и использовать их в запросах SELECT так же, как и обычные таблицы.
Представления позволяют создать слой абстракции над таблицами: даже если структура таблиц меняется, приложения и пользователи продолжают работать через представление без изменений.
Представление можно создать на основе одной или нескольких таблиц, а также других представлений.
Материализованное представление хранит как SQL-запрос, так и результаты его выполнения в структуре, похожей на таблицу. Это может значительно повысить производительность запросов, особенно для ресурсоемких операций. В отличие от обычных представлений, материализованные не обновляются автоматически — их нужно обновлять вручную, чтобы данные оставались актуальными.
Greengage DB включает встроенные системные представления.
Например, представления gp_toolkit.gp_log_* позволяют выполнять SQL-запросы к серверным логам, а pg_catalog.pg_settings отображает параметры конфигурации.
Предварительные требования
Для выполнения команд, описанных в следующих разделах, подключитесь к мастер-хосту Greengage DB с помощью psql, как описано в статье Подключение к Greengage DB с использованием psql.
Создайте новую базу данных и подключитесь к ней:
CREATE DATABASE media_library;
\c media_library
Затем создайте таблицу и заполните ее данными:
CREATE TABLE movies
(
id INTEGER,
title TEXT NOT NULL,
genre TEXT NOT NULL,
year INT,
rating NUMERIC(3, 1)
)
WITH (appendoptimized = true, orientation = row)
DISTRIBUTED BY (id);
INSERT INTO movies (id, title, genre, year, rating)
SELECT gs AS id,
'Movie ' || gs AS title,
(ARRAY ['Sci-Fi','Crime','Romance','Action','Drama'])[(random() * 4)::int + 1] AS genre,
(1925 + (random() * 100)::int) AS year,
round((7.0 + random() * 3.0)::numeric, 1) AS rating
FROM generate_series(1, 1000) gs;
Представления
Обзор синтаксиса
Чтобы создать представление, используйте команду CREATE VIEW.
Чтобы изменить определение существующего представления, используйте ALTER VIEW.
Ниже приведен упрощенный синтаксис команды CREATE VIEW:
CREATE [ TEMP ] VIEW <name>
AS <query>
Основные выражения и аргументы для определения представления:
-
TEMPСоздает временное представление, которое существует только во время сессии. Если представление ссылается на временную таблицу, оно создается как временное — независимо от того, указан
TEMPили нет. -
<name>Имя представления (при необходимости с указанием схемы), которое будет создано.
-
<query>Запрос
SELECTилиVALUES, который определяет строки и столбцы представления. Этот SQL-запрос формирует данные, которые возвращаются при обращении к представлению.
Создание представления
Эта команда создает представление, которое отображает общее количество фильмов и средний рейтинг для каждого жанра:
CREATE VIEW avg_rating_by_genre AS
SELECT genre,
COUNT(*) AS total_movies,
ROUND(AVG(rating), 2) AS avg_rating
FROM movies
GROUP BY genre;
Вы можете выполнять запросы к этому представлению как к обычной таблице с помощью SELECT:
SELECT *
FROM avg_rating_by_genre;
Результат должен выглядеть следующим образом:
genre | total_movies | avg_rating ---------+--------------+------------ Romance | 265 | 8.49 Drama | 129 | 8.45 Sci-Fi | 117 | 8.38 Action | 240 | 8.59 Crime | 249 | 8.51 (5 rows)
Эта команда создает другое представление на основе таблицы movies, которое возвращает общее количество фильмов и средний рейтинг для каждого десятилетия:
CREATE VIEW movie_stats_by_decade AS
SELECT (year / 10) * 10 AS decade,
COUNT(*) AS total_movies,
ROUND(AVG(rating), 2) AS avg_rating
FROM movies
GROUP BY decade
ORDER BY decade;
Если и CREATE VIEW <view> …, и SELECT … FROM <view> содержат выражение ORDER BY, Greengage DB игнорирует выражение ORDER BY в команде CREATE VIEW.
Создание вложенного представления
Следующая команда создает вложенное представление, которое выбирает три жанра с самой высокой средней оценкой на основе ранее определенного представления avg_rating_by_genre:
CREATE VIEW top_3_genres AS
SELECT genre, avg_rating
FROM (SELECT genre,
avg_rating,
RANK() OVER (ORDER BY avg_rating DESC) AS genre_rank
FROM avg_rating_by_genre) ranked
WHERE genre_rank <= 3;
Приведенный ниже запрос SELECT возвращает все строки и столбцы из созданного представления:
SELECT *
FROM top_3_genres;
Результат должен выглядеть следующим образом:
genre | avg_rating ---------+------------ Action | 8.59 Crime | 8.51 Romance | 8.49 (3 rows)
Получение информации о представлениях
Метакоманды psql
Вы можете использовать метакоманду \dv или \dv+, чтобы получить список представлений, например:
\dv
Результат выглядит следующим образом:
List of relations Schema | Name | Type | Owner --------+-----------------------+------+--------- public | avg_rating_by_genre | view | gpadmin public | movie_stats_by_decade | view | gpadmin public | top_3_genres | view | gpadmin (3 rows)
Метакоманда \d+ возвращает информацию о представлении avg_rating_by_genre:
\d+ avg_rating_by_genre
Результат должен включать определение представления:
View "public.avg_rating_by_genre"
Column | Type | Modifiers | Storage | Description
--------------+---------+-----------+----------+-------------
genre | text | | extended |
total_movies | bigint | | plain |
avg_rating | numeric | | main |
View definition:
SELECT movies.genre,
count(*) AS total_movies,
round(avg(movies.rating), 2) AS avg_rating
FROM movies
GROUP BY movies.genre;
Системные каталоги
-
Вы можете выполнить запрос к представлению
pg_views, чтобы получить список пользовательских представлений:SELECT schemaname, viewname, definition FROM pg_views WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit') ORDER BY schemaname, viewname;Результат запроса должен включать определение каждого представления:
schemaname | viewname | definition ------------+-----------------------+-------------------------------------------------------------------------------------- public | avg_rating_by_genre | SELECT movies.genre, + | | count(*) AS total_movies, + | | round(avg(movies.rating), 2) AS avg_rating + | | FROM movies + | | GROUP BY movies.genre; public | movie_stats_by_decade | SELECT ((movies.year / 10) * 10) AS decade, + | | count(*) AS total_movies, + | | round(avg(movies.rating), 2) AS avg_rating + | | FROM movies + | | GROUP BY ((movies.year / 10) * 10) + | | ORDER BY ((movies.year / 10) * 10); public | top_3_genres | SELECT ranked.genre, + | | ranked.avg_rating + | | FROM ( SELECT avg_rating_by_genre.genre, + | | avg_rating_by_genre.avg_rating, + | | rank() OVER (ORDER BY avg_rating_by_genre.avg_rating DESC) AS genre_rank+ | | FROM avg_rating_by_genre) ranked + | | WHERE (ranked.genre_rank <= 3); (3 rows) -
Чтобы узнать, какие представления напрямую зависят от таблицы
movies, выполните SQL-запрос, который объединяет системные таблицы с информацией о зависимостях и выбирает только представления (relkind = 'v'):SELECT DISTINCT v.oid::regclass AS view, d.refobjid::regclass AS ref_object FROM pg_depend AS d JOIN pg_rewrite AS r ON r.oid = d.objid JOIN pg_class AS v ON v.oid = r.ev_class WHERE v.relkind = 'v' AND d.classid = 'pg_rewrite'::regclass AND d.deptype = 'n' AND d.refclassid = 'pg_class'::regclass AND d.refobjid = 'movies'::regclass;DISTINCTиспользуется, чтобы избежать повторов строк, когда представление ссылается на несколько столбцов одной таблицы, так какpg_dependсоздает запись для каждого столбца. Результат выглядит так:view | ref_object -----------------------+------------ movie_stats_by_decade | movies avg_rating_by_genre | movies (2 rows)
-
Вы можете изменить предыдущий запрос, чтобы найти представления, зависящие от конкретного столбца — это полезно, если вы планируете удалить этот столбец:
SELECT v.oid::regclass AS view, d.refobjid::regclass AS ref_object, a.attname AS col_name FROM pg_attribute AS a JOIN pg_depend AS d ON d.refobjsubid = a.attnum AND d.refobjid = a.attrelid JOIN pg_rewrite AS r ON r.oid = d.objid JOIN pg_class AS v ON v.oid = r.ev_class WHERE v.relkind = 'v' AND d.classid = 'pg_rewrite'::regclass AND d.refclassid = 'pg_class'::regclass AND d.deptype = 'n' AND a.attrelid = 'movies'::regclass AND a.attname = 'genre';Результат показывает, что только одно представление зависит от столбца
genreв таблицеmovies:view | ref_object | col_name ---------------------+------------+---------- avg_rating_by_genre | movies | genre (1 row)
-
Приведенный ниже SQL-запрос выводит информацию о представлениях, которые ссылаются на другие представления. Выражение
WITHполучает все представления из пользовательских схем, а основнойSELECTнаходит представления, которые ссылаются на другие представления:WITH views AS (SELECT v.relname AS view, d.refobjid AS ref_object, v.oid AS view_oid, ns.nspname AS namespace FROM pg_depend AS d JOIN pg_rewrite AS r ON r.oid = d.objid JOIN pg_class AS v ON v.oid = r.ev_class JOIN pg_namespace AS ns ON ns.oid = v.relnamespace WHERE v.relkind = 'v' AND ns.nspname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit') AND d.deptype = 'n' AND NOT (v.oid = d.refobjid)) SELECT DISTINCT views.view, views.namespace AS schema, views.ref_object::regclass AS ref_view, ref_nspace.nspname AS ref_schema FROM views JOIN pg_depend as dep ON dep.refobjid = views.view_oid JOIN pg_class AS class ON views.ref_object = class.oid JOIN pg_namespace AS ref_nspace ON class.relnamespace = ref_nspace.oid WHERE class.relkind = 'v' AND dep.deptype = 'n';Результат должен включать представление
top_3_genres:view | schema | ref_view | ref_schema --------------+--------+---------------------+------------ top_3_genres | public | avg_rating_by_genre | public (1 row)
Удаление представления
Чтобы удалить представление, используйте команду DROP VIEW:
DROP VIEW movie_stats_by_decade;
Чтобы удалить представление вместе со связанными объектами, например, зависимыми представлениями, укажите параметр CASCADE:
DROP VIEW avg_rating_by_genre CASCADE;
Материализованные представления
Обзор синтаксиса
Чтобы создать материализованное представление, используйте команду CREATE MATERIALIZED VIEW.
Чтобы изменить определение существующего материализованного представления, используйте ALTER MATERIALIZED VIEW.
Ниже приведен упрощенный синтаксис команды CREATE MATERIALIZED VIEW:
CREATE MATERIALIZED VIEW <name>
AS <query>
[ WITH [ NO ] DATA ]
[ DISTRIBUTED ... ]
Основные выражения и аргументы для определения материализованного представления следующие:
-
<name>Имя материализованного представления (при необходимости с указанием схемы), которое будет создано.
-
<query>Запрос
SELECTилиVALUES, который формирует данные, сохраняемые в материализованном представлении. -
WITH [ NO ] DATAУказывает, должно ли материализованное представление быть заполнено данными при создании.
WITH DATAиспользуется по умолчанию и заполняет материализованное представление. -
DISTRIBUTEDТип распределения, определяющий, как строки материализованного представления распределяются по сегментам кластера Greengage DB. Узнайте подробнее в статье Распределение данных.
Создание материализованного представления
Эта команда создает материализованное представление, которое отображает общее количество фильмов и средний рейтинг для каждого жанра:
CREATE MATERIALIZED VIEW avg_rating_by_genre_mat AS
SELECT genre,
COUNT(*) AS total_movies,
ROUND(AVG(rating), 2) AS avg_rating
FROM movies
GROUP BY genre
DISTRIBUTED BY (genre);
Вы можете выполнять запросы к этому материализованному представлению как к обычной таблице с помощью SELECT:
SELECT *
FROM avg_rating_by_genre_mat
ORDER BY avg_rating DESC;
Результат должен выглядеть так:
genre | total_movies | avg_rating ---------+--------------+------------ Action | 240 | 8.59 Crime | 249 | 8.51 Romance | 265 | 8.49 Drama | 129 | 8.45 Sci-Fi | 117 | 8.38 (5 rows)
Обновление материализованного представления
Чтобы обновить материализованное представление, используйте команду REFRESH MATERIALIZED VIEW.
Можно настроить автоматическое обновление представления с использованием этой команды.
Команда ниже вставляет дополнительные данные в исходную таблицу movies, а именно фильмы с рейтингами от 9.0 до 10.0:
INSERT INTO movies (id, title, genre, year, rating)
SELECT gs AS id,
'Movie ' || gs AS title,
(ARRAY ['Sci-Fi','Crime','Romance','Action','Drama'])[(random() * 4)::int + 1] AS genre,
(1925 + (random() * 100)::int) AS year,
round((7.0 + random() * 3.0)::numeric, 1) AS rating
FROM generate_series(1, 1000) gs;
Это должно повысить средний рейтинг для каждого жанра примерно с 8.5 до 9.0.
Используйте оператор SELECT для выполнения запроса к материализованному представлению:
SELECT *
FROM avg_rating_by_genre_mat
ORDER BY avg_rating DESC;
Результат должен показать, что значения в столбце avg_rating не изменились:
genre | total_movies | avg_rating ---------+--------------+------------ Action | 240 | 8.59 Crime | 249 | 8.51 Romance | 265 | 8.49 Drama | 129 | 8.45 Sci-Fi | 117 | 8.38 (5 rows)
Обновите данные материализованного представления с помощью REFRESH MATERIALIZED VIEW:
REFRESH MATERIALIZED VIEW avg_rating_by_genre_mat;
Затем снова выполните запрос к материализованному представлению:
SELECT *
FROM avg_rating_by_genre_mat
ORDER BY avg_rating DESC;
Результаты показывают, что значения avg_rating обновились и теперь близки к 9.0:
genre | total_movies | avg_rating ---------+--------------+------------ Action | 483 | 9.05 Crime | 517 | 9.02 Drama | 262 | 8.96 Romance | 490 | 8.96 Sci-Fi | 248 | 8.94 (5 rows)
Получение информации о материализованных представлениях
Метакоманды psql
Используйте метакоманду \dm или \dm+, чтобы получить список материализованных представлений:
\dm+
Результат выглядит следующим образом:
List of relations Schema | Name | Type | Owner | Size | Description --------+-------------------------+-------------------+---------+--------+------------- public | avg_rating_by_genre_mat | materialized view | gpadmin | 256 kB | (1 row)
Метакоманда \d+ выводит информацию о материализованном представлении avg_rating_by_genre_mat:
\d+ avg_rating_by_genre_mat;
Результат включает определение материализованного представления:
Materialized view "public.avg_rating_by_genre_mat"
Column | Type | Modifiers | Storage | Stats target | Description
--------------+---------+-----------+----------+--------------+-------------
genre | text | | extended | |
total_movies | bigint | | plain | |
avg_rating | numeric | | main | |
View definition:
SELECT movies.genre,
count(*) AS total_movies,
round(avg(movies.rating), 2) AS avg_rating
FROM movies
GROUP BY movies.genre;
Distributed by: (genre)
Системные каталоги
Выполните запрос к представлению pg_matviews, чтобы получить список материализованных представлений:
SELECT matviewname, definition
FROM pg_matviews;
Результат выглядит следующим образом:
matviewname | definition
-------------------------+------------------------------------------------
avg_rating_by_genre_mat | SELECT movies.genre, +
| count(*) AS total_movies, +
| round(avg(movies.rating), 2) AS avg_rating+
| FROM movies +
| GROUP BY movies.genre;
(1 row)
Вы можете получить информацию о зависимостях материализованных представлений так же, как и для обычных представлений; единственное отличие в том, что relkind должен быть равен 'm'.
Удаление материализованного представления
Чтобы удалить материализованное представление, используйте команду DROP MATERIALIZED VIEW:
DROP MATERIALIZED VIEW avg_rating_by_genre_mat;
Чтобы удалить материализованное представление вместе со связанными объектами, например, зависимыми материализованными представлениями, укажите параметр CASCADE.