Объяснение представлений SQL: создавайте, используйте и избегайте ловушек, с примерами

  1. Главная
  2. Блог
  3. Базы данных и SQL

Базы данных и SQL

Объяснение представлений SQL: Создавайте, используйте и избегайте ловушек, с примерами

Представление — это сохраненный запрос, который можно использовать как таблицу. Узнайте, как его создать, почему представления обеспечивают согласованность отчетов, что они делают и чего не делают для повышения производительности, а также ловушки, которые их нарушают.

8 мин чтения
Объяснение представлений SQL: создавайте, используйте и избегайте ошибок, с примерами

Главное

  • Представление – это именованный сохраненный запрос, из которого можно выбирать, как из таблицы.
  • Обычное представление не хранит никаких данных, поэтому всегда показывает текущие результаты.
  • Представления упрощают сложные запросы, обеспечивают согласованность бизнес-логики и могут ограничивать доступ к столбцам.
  • Сами по себе они не выполняют запрос быстрее, хотя материализованные представления могут.

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

Мы выполнили каждый приведенный ниже пример в SQLite 3.50, и показанные результаты получены в результате этого запуска. Представления представляют собой стандартный SQL, но такие детали, как обновление через представление, материализованные представления и обработка зависимостей, различаются в зависимости от базы данных, и мы указываем на это.

Что такое вид?

Представление — это оператор SELECT, сохраненный в базе данных с именем. Когда вы запрашиваете представление, база данных выполняет этот сохраненный запрос. Он действует как таблица для чтения, но обычное представление не содержит собственной копии данных.

Представьте себе сохраненный фильтр в электронной таблице. Фильтр не является второй копией листа. Это способ просмотра одних и тех же данных, который всегда отражает последние значения.

Примеры таблиц

CREATE TABLE customers (
  id INT PRIMARY KEY, name TEXT, city TEXT, email TEXT, credit_limit INT
);
CREATE TABLE orders (
  id INT PRIMARY KEY, customer_id INT, total INT, status TEXT
);

INSERT INTO customers VALUES
 (1,'Asha','Pune','asha@example.com',50000),
 (2,'Ben','Delhi','ben@example.com',20000),
 (3,'Chitra','Pune','chitra@example.com',80000);

INSERT INTO orders VALUES
 (101,1,1200,'paid'), (102,1,800,'paid'),
 (103,2,300,'pending'), (104,3,2500,'paid');

Создать представление

В этом представлении суммируются заказы каждого клиента. Он скрывает объединение, группировку и выражение CASE за одним простым именем:

CREATE VIEW customer_sales AS
SELECT c.id, c.name, c.city,
       COUNT(o.id) AS orders,
       COALESCE(SUM(CASE WHEN o.status = 'paid' THEN o.total END), 0) AS paid_total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name, c.city;

Теперь любой может использовать его, даже не зная, как он работает:

SELECT * FROM customer_sales ORDER BY paid_total DESC;

-- 3 | Chitra | Pune  | 1 | 2500
-- 1 | Asha   | Pune  | 2 | 2000
-- 2 | Ben    | Delhi | 1 |    0

SELECT name, paid_total FROM customer_sales WHERE city = 'Pune';

-- Asha   | 2000
-- Chitra | 2500

Бен отображается с нулем, поскольку в представлении используются LEFT JOIN и COALESCE. Подобные решения, принятые однажды в представлении, — это именно то, что обеспечивает согласованность отчетов. Чтобы освежить понимание обработки NULL, см. Значения SQL NULL.

Просмотры всегда актуальны

Поскольку обычное представление выполняет запрос каждый раз, новые данные появляются немедленно. Мы добавили оплаченный заказ для Бена и снова запросили представление:

INSERT INTO orders VALUES (105, 2, 900, 'paid');

SELECT name, paid_total FROM customer_sales WHERE name = 'Ben';

-- Ben | 900

Не было ни шага обновления, ни копии для синхронизации. В этом ключевое отличие от сохранения результатов запроса в отдельную таблицу.

Скрытие столбцов

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

CREATE VIEW public_customers AS
SELECT id, name, city FROM customers;

-- columns: id, name, city

В базах данных с разрешениями, таких как PostgreSQL, SQL Server и MySQL, вы можете предоставить пользователю отчетов доступ к представлению, но не к базовой таблице. В SQLite нет учетных записей пользователей, поэтому мы могли показать только само ограничение столбца. Рассматривайте представление как один уровень защиты. Конфиденциальные данные по-прежнему требуют надлежащего контроля доступа, а некоторые базы данных обеспечивают безопасность на уровне строк для фильтрации строк по пользователю.

Производительность: просмотры не повышают скорость

Распространенным мифом является то, что представления делают запросы быстрее. Обычное представление заменяется его определением при запросе, поэтому база данных выполняет ту же работу, как если бы вы написали полный запрос самостоятельно. Наш план выполнения показал, что объединение и группировка представлений выполняются как часть запроса:

EXPLAIN QUERY PLAN SELECT * FROM customer_sales WHERE city = 'Pune';

-- CO-ROUTINE customer_sales
-- SCAN c ...
-- SEARCH o USING AUTOMATIC COVERING INDEX (customer_id=?) LEFT-JOIN
-- USE TEMP B-TREE FOR GROUP BY
-- SCAN customer_sales

Таким образом, просмотр медленного запроса по-прежнему остается медленным запросом. Скорость зависит от индексов и хорошего дизайна запросов, как описано в Оптимизация SQL-запросов и Объяснение индексов SQL. Также следите за представлениями, построенными на основе других представлений. Слои вложенности позволяют легко и незаметно присоединиться к одной и той же большой таблице несколько раз.

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

Материализованное представление сохраняет свои результаты, как кэшированная таблица. Чтение происходит быстро, но данные остаются свежими только после последнего обновления.

-- PostgreSQL
CREATE MATERIALIZED VIEW daily_sales AS
SELECT order_date, SUM(total) AS revenue FROM orders GROUP BY order_date;

REFRESH MATERIALIZED VIEW daily_sales;
Особенность Обычный вид Материализованный вид
Хранит данные Нет Да
Свежесть Всегда актуальный На момент последнего обновления
Скорость чтения То же, что и запрос Быстро
Стоимость хранения Нет Да
Хорошо для Упрощение, последовательность Тяжелые отчеты и информационные панели

Поддержка варьируется. PostgreSQL и Oracle имеют материализованные представления, а SQL Server предлагает индексированные представления со своими собственными правилами. В SQLite и MySQL их нет, поэтому люди используют обычную сводную таблицу, обновляемую по расписанию. Для этой статьи мы не запускали пример PostgreSQL.

Совет. Прежде чем создавать материализованное представление, решите, насколько устаревшими могут быть данные. «Обновляется каждый час» должно быть приемлемо для людей, читающих отчет, поэтому скажите им.

Можете ли вы обновить представление?

Это зависит от базы данных и представления. Простое представление одной таблицы без группировки или DISTINCT часто можно обновить в PostgreSQL, MySQL и SQL Server, и изменения передаются в таблицу. Представление с объединениями или агрегатами обычно не может этого сделать, поскольку база данных не может определить, какую строку следует изменить.

SQLite вообще не допускает прямую запись в представления. Наша попытка не удалась:

INSERT INTO customer_sales (name) VALUES ('X');

-- cannot modify customer_sales because it is a view

Некоторые базы данных поддерживают триггеры INSTEAD OF, позволяющие сделать представление доступным для записи, а многие предлагают WITH CHECK OPTION, который останавливает запись, которая не была бы видна через представление. На практике большинство команд сохраняют представления только для чтения и записывают данные в таблицы.

Зависимости и поломки

Представление зависит от таблиц, из которых оно считывается. Мы создали представление таблицы, а затем удалили ее:

CREATE TABLE ghost AS SELECT 1 AS x;
CREATE VIEW v_ghost AS SELECT x FROM ghost;
DROP TABLE ghost;

SELECT * FROM v_ghost;
-- no such table: main.ghost

SQLite разрешил удаление и потерпел неудачу только позже, когда использовалось представление. Другие базы данных, такие как PostgreSQL, отказываются удалять таблицу, от которой зависит представление, если вы не добавите CASCADE, что также удалит представление. В любом случае переименование или удаление столбца может привести к поломке представлений, поэтому проверьте их при регрессионном тестировании после изменения схемы.

Существует также тонкая ловушка с SELECT * в представлении. Мы создали CREATE VIEW star AS SELECT * FROM customers, затем добавили столбец в таблицу. В SQLite представление подобрало новый столбец. Другие базы данных, включая PostgreSQL, расширяют список столбцов при создании представления, поэтому новые столбцы не появляются. Поэтому перечислите столбцы явно. Он документирует представление и везде ведет себя одинаково.

Управление представлениями

Задача Общий синтаксис
Создать CREATE VIEW name AS SELECT ...
Заменить CREATE OR REPLACE VIEW (PostgreSQL, MySQL, Oracle)
Заменить в SQL Server СОЗДАТЬ ИЛИ ИЗМЕНИТЬ ПРОСМОТР
Удалить ОТПРАВИТЬ ПРОСМОТР имени
Безопасное удаление ОТКЛЮЧИТЬ ПРОСМОТР, ЕСЛИ СУЩЕСТВУЕТ имя

При удалении представления удаляется только сохраненный запрос. В нашем тесте таблица customers все еще содержала все три строки после удаления public_customers. Сохраняйте определения представлений в системе контроля версий вместе с миграциями, чтобы изменения проверялись так же, как и любой другой код.

Просмотр, CTE и таблица

Вариант Срок службы Хранит данные Лучшее для
Посмотреть Пока не упал Нет Общая логика, используемая многими запросами
КТР Одно заявление Нет Организация одного сложного запроса
Материализованный вид Пока не упал Да Быстрое считывание дорогостоящих результатов
Таблица Пока не упал Да Фактические данные

Распространенные ошибки

  • Ожидается увеличение скорости. Обычное представление выполняет запрос каждый раз.
  • Использование SELECT * в представлениях. Поведение разных баз данных при изменении таблиц различается.
  • Глубокая вложенность. Представления, построенные на основе представлений, скрывают дорогостоящие соединения.
  • Использование ORDER BY внутри представления. Порядок не гарантируется при запросе, поэтому сортируйте внешний запрос.
  • Непонятные имена. Поможет префикс, например v_, или ясное существительное, например customer_sales.
  • Забываем представления во время миграции. Проверяйте их после каждого изменения схемы.

Итоги

Представления — это простой способ разместить важную логику в базе данных. Используйте их для обеспечения единообразия отчетов и скрытия ненужных столбцов, а не для снижения производительности. Для начала найдите запрос, который появляется в нескольких местах вашего кода, превратите его в представление с явными столбцами и укажите на него каждому вызывающему объекту.

Часто задаваемые вопросы

Что такое представление в SQL?

Представление — это сохраненный запрос SELECT с именем. Вы запрашиваете ее как таблицу, и база данных каждый раз выполняет базовый запрос, поэтому результаты всегда актуальны.

Хранит ли представление SQL данные?

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

Представления работают быстрее, чем таблицы?

Нет. Обычное представление выполняет ту же работу, что и лежащий в его основе запрос. Материализованные представления могут ускорить чтение за счет хранения и актуальности.

Можете ли вы вставить в представление?

Иногда. Простые однотабличные представления часто можно обновлять в PostgreSQL, MySQL и SQL Server. Представления с объединениями или группировками обычно этого не делают, и SQLite не позволяет этого.

Что произойдет с представлением, если таблицу удалить?

Это зависит от базы данных. Некоторые отказываются удалять таблицу, пока от нее зависит представление, в то время как другие разрешают это, и представление завершается с ошибкой при запросе.

Продолжайте учиться

Работайте с Stack Quora

Нужна помощь с тестированием, WordPress или SEO?

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

Продолжайте читать

Больше руководств по тестирование, искусственный интеллект и современные разработки

Понравилась эта статья? Выберите тему и сразу приступайте к нашим новейшим руководствам.

Искусственный интеллект

Объяснение галлюцинаций искусственного интеллекта: почему языковые модели все выдумывают и как их уменьшить

Галлюцинации ИИ объяснили: что это такое, почему языковые модели их создают, распространенные типы, как уменьшить их с помощью заземления и подсказо…

Читать статью →
DevOps

Объяснение переменных среды: файлы .env, секреты и правильно выполненная конфигурация

Переменные среды объясняются на примерах Python, которые мы запускали: их чтение и установка, почему каждое значение является строкой, файлы .env, н…

Читать статью →
Разработка ПО

Объяснение ограничения скорости API: алгоритмы, заголовки и обработка ошибок 429

Ограничение скорости API объясняется с помощью проведенного нами моделирования: сегмент токенов, фиксированное окно, скользящее окно, 429 ответов, з…

Читать статью →
Автоматизация тестирования

Издевательство в Python: unittest.mock, патч и ошибки, из-за которых тесты лгут

Издевательство на Python с помощью unittest.mock, протестировано: Mock и return_value, Side_effect, исправление использования имени, спецификация и …

Читать статью →
Тестирование ПО

Объяснение покрытия кода: покрытие линий или ветвей и что на самом деле означает 100%

Покрытие кода объяснено с помощью реального запуска покрытия.py: покрытие строк, ветвей и условий, чтение отчета, почему 100%-ное покрытие все еще м…

Читать статью →
Базы данных и SQL

DELETE, TRUNCATE и DROP в SQL: различия, откат и когда использовать каждый из них

Объяснение DELETE, TRUNCATE и DROP на примере протестированного SQL: что каждый из них удаляет, откат, триггеры, сброс идентификаторов, скорость и ч…

Читать статью →
Базы данных и SQL

Объяснение представлений SQL: создавайте, используйте и избегайте ловушек, с примерами

Представления SQL, объясненные с помощью запросов, которые мы выполняли: создание представления, результаты в реальном времени, скрытие столбцов, пр…

Читать статью →
Разработка ПО

Интерфейс TypeScript и тип: реальные различия, протестированные с помощью компилятора

Интерфейс TypeScript против типа, проверенный реальным компилятором: что могут делать оба, что может делать только один, слияние объявлений, расшире…

Читать статью →
Разработка ПО

Объяснение Python *args и **kwargs: гибкие функции с понятными примерами

Python *args и **kwargs, объясненные с помощью кода, который мы запускали: упаковка и распаковка, порядок параметров, аргументы только по ключевым с…

Читать статью →
Разработка ПО

Лямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать

Лямбда-функции Python, объясненные с помощью кода, который мы запускали: синтаксис, сортировка с помощью ключа, карты и фильтра, замыкания, ловушка …

Читать статью →
Искусственный интеллект

Что такое ИИ-агенты? Как они работают: можно запустить простой цикл агента

Что такое агенты ИИ? Простое руководство по циклу агента, инструментам, памяти и ограничениям, с небольшим циклом Python, который мы запустили, реал…

Читать статью →
DevOps

Мониторинг и наблюдаемость: реальная разница с журналами, метриками и трассировками

Объяснение мониторинга и наблюдаемости: известные и неизвестные сбои, журналы, метрики и трассировки, пошаговое руководство по инцидентам при оформл…

Читать статью →
DevOps

Docker Volumes против Bind Mounts: правильное сохранение данных контейнера

Объяснение томов Docker и монтирования привязки: почему исчезают данные контейнера, именованные тома, монтирование привязки, tmpfs, примеры создания…

Читать статью →
Автоматизация тестирования

TDD против BDD: ключевые различия, одна функция, созданная для обоих способов, и когда использовать каждый

TDD и BDD объясняются с помощью одной функции скидки, созданной в обоих направлениях: красно-зеленый рефакторинг с кодом, который мы запускали, сцен…

Читать статью →
Разработка ПО

Git merge vs rebase: отличия, примеры и когда что использовать

Git merge vs rebase с реальным выводом log, сравнительной таблицей, золотым правилом rebase и безопасным рабочим процессом для команды.

Читать статью →
Разработка ПО

Генераторы списков в Python: синтаксис, примеры и когда их избегать

Генераторы списков в Python: синтаксис, фильтры, if-else, вложенные циклы, измеренное время и случаи, когда обычный цикл for лучше.

Читать статью →
Базы данных и SQL

SQL JOIN: объяснение с примерами (Inner, Left, Right, Full)

Разберите SQL JOIN шаг за шагом на примерах таблиц и запросов: INNER, LEFT, RIGHT, FULL, CROSS и SELF, а также частые ошибки и советы по производите…

Читать статью →