Инкапсулируйте сложные запросы, используя возможности представлений.

Postgres предлагает два типа представлений: обычные представления и материализованные представления. Оба могут решать аналогичные проблемы, связанные с инкапсуляцией, абстракцией и повышением производительности.

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

Давайте сначала определим, что такое представление в Postgres.

Взгляды

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

Это простое определение представления, это просто SQL. Но в чем преимущество использования представления?

Что ж, это помогает инкапсулировать и абстрагироваться от сложных SQL-запросов, которые могут жить в моделях или сервисных объектах. Это также упрощает процесс изменения SQL-запроса, поскольку есть только одно место, куда можно перейти и внести некоторые изменения.

Представьте, что если вы используете один и тот же SQL-запрос во многих местах своего приложения, всякий раз, когда вам нужно переписать запрос и внести изменения, вы должны найти все места, где вы его используете. Это не очень подходит.

Преимущества использования представления

  1. Инкапсулируйте сложные запросы.
  2. Абстрактное объединение, группировка по, где, по порядку и любой другой оператор SQL.
  3. Повторно используйте сложный запрос в приложении.
  4. Избавьтесь от SQL-запросов внутри моделей или служебных объектов.
  5. Сделайте свой код чище.

Как я уже упоминал, представление - это таблица, к которой мы можем получить доступ, используя модель. Но мы можем только запрашивать представление, но не выполнять операции создания, обновления или удаления, потому что это не настоящая таблица, это просто набор SQL, который запускается каждый раз, когда мы обращаемся к модели.

Теперь, когда определение и преимущества использования представления ясны, давайте создадим приложение rails.

Описание Проекта

В этом проекте мы собираемся использовать представление для инкапсуляции сложного SQL-запроса. По этому запросу будут получены обзоры телешоу. Я воспользуюсь драгоценным камнем под названием scenic, чтобы создать вид.

rails new tv_shows --api -d=postgresql -T

Давайте создадим базу данных.

rails db:create

Теперь сгенерируем модели.

rails g model TvShow name show_type
rails g model Group name contact email
rails g model Team name color description status:integer group:references
rails g model User name email status:integer team:references
rails g model ReviewType name code
rails g model Review description title scheduled_date:datetime votes:integer revenue:integer status:integer user:references review_type:references tv_show:references

Поскольку приложение позволяет пользователю при желании принадлежать к группе, группа, к которой может принадлежать группа, необязательно принадлежит группе, а рецензия необязательно принадлежит типу обзора или пользователю. Не забудьте изменить флаг null: false на true для этих foreign_keys для каждой миграции.

Вы можете ознакомиться с миграциями здесь.

Давайте перенесем БД.

rails db:migrate

Определение ассоциаций между моделями следующее:

Я воспользуюсь исходным файлом, чтобы вставить данные в БД. Скопируйте содержимое файла seed.rb из репо и вставьте его в свой исходный файл.

Прежде чем заполнять БД, добавьте фейкер в Gemfile в тестовой группе разработки.

gem "faker"

Установите драгоценный камень, а затем заполните БД.

bundle install
rails db:seed

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

rails g controller Reviews index

Внутри файла routes.rb измените маршрут для этого.

get 'tv_shows/:id/reviews', action: :index, controller: :reviews

Внутри контроллера поместите следующий код:

Здесь я использую служебный объект для создания обзоров, поэтому контроллер несет ответственность только за предоставление обзоров, а тяжелая работа делегируется служебному объекту.

В папке приложения создайте папку служб, а внутри создайте новый файл reviews_service.rb

Этот объект службы имеет метод класса, который получает параметры и инициализирует класс. Это просто способ абстрагироваться от шагов инициализации. Затем сервис вызывает метод get_reviews.

Здесь все начинает усложняться. Этот метод имеет длинный и сложный SQL-запрос. Вдобавок его действительно трудно читать, и из-за этого сервис выглядит некрасиво.

Пока не волнуйтесь !. Скоро мы это исправим.

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

rails s
http://localhost:3000/tv_shows/1/reviews

А теперь давайте воспользуемся представлением, чтобы решить эту уродливую проблему.

Внутри файла драгоценного камня добавьте живописный драгоценный камень.

gem 'scenic', '~> 1.5', '>= 1.5.4'

Установите драгоценный камень

bundle install

Создайте новое представление, я назову его latest_reviews.

rails generate scenic:view latest_reviews

Это создаст два файла.

  1. db / views / latest_reviews_v01.sql
  2. db / migrate / timestamp_create_latest_reviews.rb

Файл миграции содержит код для создания представления в Postgres. Мы не будем изменять этот файл.

В файле SQL мы будем писать запрос, который хотим запускать каждый раз, когда вызываем это представление.

Итак, что мы собираемся сделать, это взять этот сложный SQL-запрос и преобразовать его в чистый SQL. Окончательный запрос выглядит так.

Теперь мы можем перенести представление.

rails db:migrate

Как я уже упоминал, для взаимодействия с этим представлением нам нужна модель, поэтому давайте создадим ее. Внутри папки моделей создайте новый файл.

touch latest_review.rb

А выглядит модель вот так.

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

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

Теперь, в модели tv_show, нам также нужно добавить эту ассоциацию.

has_many :latest_reviews

В служебном объекте мы можем заменить запрос SQL и вместо этого просто вызвать ассоциацию latest_reviews в объекте телешоу.

tv_show.latest_reviews

С этим изменением объект службы выглядит так.

В этом сила представления в PostgreSQL. Мы абстрагировали уродливый и сложный SQL-запрос от объекта службы, а теперь мы можем повторно использовать этот запрос повсюду, потому что это ассоциация.

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

Здорово! Если мы перезапустим сервер rails и снова попадем в конечную точку, мы должны увидеть тот же результат, что и раньше.

Вы можете найти код этого проекта здесь.

Вывод

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

Есть взгляд со сверхспособностями, и это материализованный взгляд. Если вы хотите увидеть, как материализованные представления делают еще один шаг в повышении производительности, ознакомьтесь со статьей Материализованные представления и задания Sidekiq с Rails.

Надеюсь, вам удобнее использовать представления в своих приложениях, спасибо за чтение!