Введение
Представление (VIEW) — сохранённый, поимённый запрос, который можно использовать подобно обычной таблице в других запросах — в статье разберём, как представления помогают инкапсулировать сложную логику запроса за простым, многократно используемым именем, и какие ограничения у этого подхода.
Концепция
В отличие от CTE (статья 405), существующего только в рамках одного конкретного запроса, представление сохраняется в схеме БД постоянно и доступно для использования в любом количестве разных запросов в будущем, аналогично таблице — но в отличие от реальной таблицы, представление не хранит данные физически (за исключением материализованных представлений, статья 407): каждое обращение к обычному представлению фактически выполняет заново лежащий в его основе запрос. Представления полезны для инкапсуляции часто повторяющейся сложной логики (множественные JOIN, агрегация) за простым именем, и для управления доступом — предоставление пользователю/приложению доступа к представлению, показывающему лишь подмножество столбцов/строк реальной таблицы, без прямого доступа к самой таблице.
Пример кода: Представления (VIEW)
-- Представление, инкапсулирующее часто повторяющийся, сложный запрос
CREATE VIEW customer_order_summary AS
SELECT c.id, c.name, c.email,
COUNT(o.id) AS order_count,
COALESCE(SUM(o.total_amount), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name, c.email;
-- Использование представления — выглядит как обращение к обычной таблице
SELECT * FROM customer_order_summary WHERE total_spent > 5000;
-- Представление для ограничения доступа — скрытие чувствительных столбцов
CREATE VIEW customers_public AS
SELECT id, name, registered_at -- email и другие чувствительные поля исключены
FROM customers;
-- Предоставление прав доступа только к представлению, а не к исходной таблице (статья 434)
GRANT SELECT ON customers_public TO analytics_role;
-- Обновляемое представление (updatable view) — для достаточно простых представлений
-- некоторые СУБД позволяют выполнять INSERT/UPDATE через представление напрямую
CREATE VIEW active_customers AS
SELECT id, name, email FROM customers WHERE is_active = true;
UPDATE active_customers SET email = 'new@example.com' WHERE id = 1;
-- ^ работает для простых представлений (один источник, без агрегации) в большинстве СУБД,
-- но НЕ сработает для представления customer_order_summary с агрегацией выше
Пояснения к коду
customer_order_summary показывает основную ценность представлений — пользователь, обращающийся к этому представлению, не должен знать или повторять сложную логику JOIN/агрегации каждый раз заново; вместо этого он просто пишет SELECT * FROM customer_order_summary WHERE ..., как если бы это была обычная, простая таблица, а вся сложность скрыта внутри определения представления, написанного и проверенного один раз. customers_public/GRANT SELECT ON customers_public демонстрирует паттерн контроля доступа через представления — вместо предоставления прямого доступа к таблице customers (со всеми её потенциально чувствительными столбцами вроде email) определённой роли пользователей, доступ предоставляется только к ограниченному, «урезанному» представлению, физически не дающему возможности увидеть скрытые столбцы независимо от прав на само представление.
Подводные камни
- Предположение, что представление автоматически кеширует/ускоряет результат запроса, по аналогии с тем, как иногда ошибочно воспринимаются представления — обычное (не материализованное) представление НЕ хранит данные и НЕ кеширует результат: каждое обращение к нему заново выполняет весь лежащий в его основе запрос со всеми его JOIN и агрегациями; для реального кеширования результата нужно материализованное представление (статья 407), концептуально и по производительности совершенно отличное от обычного.
- Создание глубоко вложенных цепочек представлений (представление, обращающееся к другому представлению, обращающемуся к третьему, и так далее) без понимания накопленной сложности итогового запроса, который реально выполняется СУБД при обращении к самому внешнему представлению этой цепочки — хотя такая вложенность синтаксически допустима и может казаться удобной для постепенного наращивания логики, итоговый, фактически выполняемый план запроса (статья 416) может оказаться значительно сложнее и менее эффективным, чем кажется из простого, «линейного» вида каждого отдельного представления в цепочке.
- Попытка модифицировать данные через сложное представление (с JOIN, агрегацией,
DISTINCT), ожидая того же поведения, что и для простого, «прозрачного» представления над одной таблицей — большинство СУБД ограничивают возможностьINSERT/UPDATE/DELETEнепосредственно через представление лишь достаточно простыми случаями (какactive_customersв примере), и попытка модифицировать данные через представление с агрегацией или несколькими объединёнными таблицами обычно либо явно запрещена СУБД, либо требует дополнительной логики (INSTEAD OFтриггеры, статья 410) для определения, как именно такая модификация должна транслироваться в изменения базовых таблиц. - Изменение структуры базовой таблицы без проверки и обновления зависящих от неё представлений — переименование или удаление столбца, на который ссылается определение существующего представления, «ломает» это представление (попытка обратиться к нему даст ошибку, ссылающуюся на отсутствующий столбец), и перед изменением структуры таблиц стоит явно проверить, какие представления от них зависят, особенно в крупных схемах с множеством накопленных за время существования проекта представлений.