惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
МАТЕРИАЛИЗОВАННЫЕ ПРЕДСТАВЛЕНИЯ В HQBIRD SERVER 5.0.4
Gallemar · 2026-05-11 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение14 мин

Охват и читатели71

Обзор

В последней версии HQBird Server 5.0.4 появились так называемые материализованные представления. В данном вебинаре мы поговорим о том, что это такое, как с ними работать, как вы можете использовать их у себя для ускорения некоторых аналитических выборок. Будет представлен небольшой бенчмарк, который показывает, как материализованные представления могут ускорять некоторые аналитические выборки.

Что такое материализованные представления?

  • Материализованные представления представляют собой гибрид представлений и стандартной постоянной таблицы. 

  • Материализованные представления вы можете использовать только для чтения, то есть делать из них только SELECT. В них нельзя напрямую делать INSERT, UPDATE и DELETE. 

  • Для обновления материализованных представлений используются специальные команды: REFRESH MATERIALIZED VIEW и REFRESH MATERIALIZED VIEW CONCURRENTLY. Это не DML-команды, а DDL, поэтому напрямую использовать их внутри PSQL нельзя. 

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

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

  • Функции автоматического обновления материализованных представлений при изменении базовых таблиц, как это, например, есть в опции ON COMMIT, у нас нет. Но, с другой стороны, хочу сказать, что даже в Oracle эта функция очень сильно ограничивает сами запросы, в которых такие штуки могут работать. То есть она работает далеко не всегда.

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

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

Обычные представления автоматически отслеживают любые изменения, потому что вы просто заново выполняете полностью запрос. Материализованные представления этого не делают. Чтобы получить новые данные в материализованных представлениях, необходимо их обновить с помощью команды REFRESH MATERIALIZED VIEW. Если для обычного представления вы можете написать триггеры и сделать к ним через INSERT, UPDATE, DELETE соответствующее обновление базовых таблиц, то для материализованных представлений такой возможности нет. Но зато для материализованных представлений вы можете создавать разные индексы.

Как мы жили раньше?

Чем заменяли раньше материализованные представления? Это можно было делать только вручную, написав довольно-таки много кода. Обычно создавалась обычная таблица с полями, идентичными по составу полям запроса и  писалась хранимая процедура, которая обновляла эту таблицу. В HQBird есть также вариант, когда можно создавать таблицу с полями, идентичными запросу, с данными из этого запроса -  специальный вид команды CREATE TABLE AS SELECT. Минусы этого подхода состоят в том, что очищать такую таблицу, если нужно её полностью обновить, очень дорого, т.к. это реальное выполнение команды DELETE с соответствующим порождением версий и мусора. Трудно следить за типами и составом столбцов таблиц - если вы решили добавить какое-то новое поле в такое псевдоматериализованное представление, вам нужно добавить его в таблицу, в запросе его добавить, в процедуре учесть обновление этих материализованных таблиц. Были и определённые плюсы -  можно написать триггер для базовых таблиц, которые обновляют это псевдоматериализованное представление.

CREATE TABLE AS SELECT

CREATE [GLOBAL TEMPORARY] TABLE <table_name>

    [(column_name [, ...])]

	AS (<select>)

	[WITH [NO] DATA]

	[ON COMMIT PRESERVE | DELETE ROWS]

Сделаем небольшое отступление про CREATE TABLE AS SELECT. Она тоже появилась в HQBird Server 5.0.4 и позволяет создать таблицу с полями, идентичными по составу и типу полям, которые указаны в запросе SELECT. Можно создать эту таблицу с такими же полями, либо сразу её наполнить данными. Если по коммиту из временной таблицы данные удаляются, то данные её никогда не переносят. Но здесь есть определенный плюс - это быстрее, чем просто сделать CREATE и последующий INSERT INTO ... SELECT, потому что в данном случае у нас нет конкуренции за страницами. Там используется специальный вид вставки, так называемый BULK INSERT, когда страница выделяется в эксклюзивном режиме. Поскольку во время создания с этой таблицей никто не может ничего делать, то данные в неё заливаются гораздо быстрее. Индексы при этом не копируются. Копируются только типы полей и NOT NULL.

Сценарии использования материальных представлений

Теперь перейдём к самим представлениям. Для чего они могут использоваться? Для расчёта хранимых агрегатов по каким-то закрытым периодам, предварительных расчётов по редко изменяемым данным, для создания витрины для дальнейшего анализа. Можно делать либо каждый раз давая команду для обновления, либо написать сценарий, который по cron’у будет обновлять материализованные представления.

Синтаксис создания представлений

CREATE [OR ALTER] MATERIALIZED VIEW <view_name> [(columns_names)] 

  AS <query> 

  [WITH [NO] DATA]

  

ALTER MATERIALIZED VIEW <view_name> [(columns_names)] 

  AS <query> 

  [WITH [NO] DATA]

RECREATE MATERIALIZED VIEW <view_name> [(columns_names)] 

  AS <query> 

  [WITH [NO] DATA]

DROP VIEW <view_name>

По умолчанию используется WITH NO DATA!

Здесь указан синтаксис создания представлений:

В зависимости от предложения WITH DATA и WITH NO DATA, материализованные представления сразу будут содержать данные, либо они будут созданы без данных, а данные будут наполняться позже командой REFRESH MATERIALIZED VIEW. Тут также указано, что имена полей из запроса можно переопределять, если вы их укажете в списке столбцов, как для обычного представления.

VIEW <-> MATERIALIZED VIEW

ALTER MATERIALIZED VIEW <view_name> [(columns_names)] 

  AS <query> 

  TO NOT MATERIALIZED

ALTER VIEW <view_name> [(columns_names)] 

  AS <query> 

TO MATERIALIZED

Есть полезная такая вещь, как преобразование материализованного представления в обычное представление и, наоборот, преобразование обычного представления в материализованное. Для чего это может быть полезно? У вас, допустим, есть какое-то представление, которое долго считается. Вы решили попробовать его сделать материализованным и посмотреть, как оно отработает. Преобразуете, смотрите, понравилось, не понравилось, вернули обратно. Кроме того, вернуть обратно может помочь вам, если вы решили какую-то миграцию сделать куда-то где этих материализованных представлений нет. То есть вы преобразуете материализованное представление в обычное представление, делаете backup и restore и работаете на какой-то предыдущей версии, например, HQBird или на ванильной версии Firebird.

Синтаксис обновления данных

REFRESH MATERIALIZED VIEW <view_name> 

[CONCURRENTLY | DROP DATA] [CASCADE]

Есть несколько опций: CONCURRENTLY, DROP DATA, CASCADE. Сейчас я подробно о них расскажу, что они означают. 

REFRESH MATERIALIZED VIEW <view_name>

Обычно, когда никаких опций не указано, команда REFRESH MATERIALIZED VIEW требует эксклюзивного доступа к материализованному представлению. Это значит, что в это время не должно быть никаких активных запросов к материализованному представлению. Только в этом случае можно его обновить. В противном случае будет выдана ошибка, что представление материализованное используется. Как оно выполняется? Деактивируются все индексы, которые созданы для материализованного представления. Удаляются все данные из этого представления,делается это очень быстро, похоже на TRUNCATE TABLE, то есть там просто странички помечаются свободными, поэтому это очень быстрый процесс. Далее таблица заполняется новыми данными. Опять же, это делается очень быстро, потому что в данном режиме нет конкуренции за страницами данных и они немного по-другому выделяются, тут такой же принцип, как CREATE TABLE AS SELECT и BULK INSERT. И далее, если на этом материализованном представлении были какие-то индексы, то они перестраиваются. Надо сказать, что этот способ является самым быстрым для обновления большого количества данных материализованного представления. Но, с другой стороны, оно требует эксклюзивного режима доступа.

REFRESH MATERIALIZED VIEW <view_name> CONCURRENTLY

Для устранения этого недостатка есть обновление материализованного представления в конкурентном доступе, то есть здесь добавляется ключевое слово CONCURRENTLY. Оно позволяет обновлять материализованное представление и  в это время какие-то запросы, если начали работать с материализованным представлением, они просто работают со старыми данными. Такой режим требует, чтобы для материализованного представления обязательно был создан хотя бы один уникальный индекс. Лучше вообще, если он один, чтобы не запутать оптимизатор. Кроме того, эта команда обязательно требует, чтобы вы не стирали исходный запрос этого материализованного представления. Поскольку в данном режиме исходный запрос, который сделан для построения материализованного представления, он преобразуется в другие запросы, чтобы обновлять само материализованное представление. То есть внутри этой команды делается что-то подобное MERGE от запроса внутри материализованного представления к таблице, которая скрыта за кадром этого материализованного представления. Такой режим хорош, если необходимо обновить небольшое количество записей в материализованном представлении. И при этом с самим представлением ведётся активная работа, т.е. какие-то к нему есть SELECT-запросы.

REFRESH MATERIALIZED VIEW <view_name> DROP DATA

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

REFRESH MATERIALIZED VIEW … CASCADE

Ещё один режим – это добавление ключевого слова CASCADE. Обновление материализованного представления с ключевым словом CASCADE позволяет обновить не только данные в конкретном материализованном представлении, но и обновить данные в материализованных представлениях, которые задействованы в запросе текущего материализованного представления. То есть когда запрос для создания одного материализованного представления использует какие-то другие, одно или несколько материализованных представлений. Данная команда может использоваться совместно с ключевыми словами CONCURRENTLY или DROP DATA. Соответственно, CONCURRENTLY CASCADE требует уникального индекса для всех материализованных представлений, от которых зависят текущие материализованные представления и производится конкурентно, то есть не требует эксклюзивного доступа. DROP DATA CASCADE удаляет данные не только из текущего материализованного представления, но и из всех представлений, от которых зависит текущее. Соответственно, делает это быстро, но требует эксклюзивного доступа.

MATERIALIZED VIEW в GBAK

Как материализованные представления поддерживаются в GBAK? Естественно, сами данные материализованных представлений не попадают в резервную копию, но метаданные копируются. При восстановлении материализованное представление сначала восстанавливается по его метаданным, и в конце процесса восстановления запускается команда REFRESH MATERIALIZED VIEW CASCADE для всех материализованных представлений. В gbak также есть переключатель -NO_M(ATVIEWS), который позволяет не обновлять представления при восстановлении. То есть у вас база данных восстанавливается, метаданные для материализованного представления есть, но данных в них нет, вы потом можете просто в основной базе данных сделать REFRESH MATERIALIZED VIEW. В сервисах есть соответствующий параметр isc_spb_res_no_matviews

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

Практика. Остатки товаров

Теперь посмотрим на некий бенчмарк, который показывает, как можно использовать материализованные представления и скорость выполнения. Есть некоторая база данных, которая хранит приход и расход товара. То есть товары продаются, лежат на складе, и она за 10 лет. У нас 10 тысяч продуктов, и инвойсов  11 миллионов. 

  • База данных хранит продажу и приход товара за 10 лет

  • Пример искусственный

  • PRODUCT10000 записей

  • INVOICE – 1100000 записей

  • INVOICE_LINE – 11545586 записей

  • PRODUCT_STORE – 4810985 записей

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

WITH

  T AS (

    SELECT S.PRODUCT_ID, SUM(S.QUANTITY) AS QUANTITY

    FROM PRODUCT_STORE S

    GROUP BY 1

    UNION ALL

    SELECT L.PRODUCT_ID, -SUM(L.QUANTITY) AS QUANTITY

    FROM INVOICE_LINE L

    GROUP BY 1

  ),

  REMAIN_PRODUCTS AS (

    SELECT PRODUCT_ID, SUM(QUANTITY) AS QUANTITY

    FROM T

    GROUP BY 1

  )

SELECT

  REMAIN_PRODUCTS.PRODUCT_ID,

  PRODUCT.NAME AS PRODUCT_NAME,

  REMAIN_PRODUCTS.QUANTITY

FROM

  REMAIN_PRODUCTS

  JOIN PRODUCT ON PRODUCT.PRODUCT_ID = REMAIN_PRODUCTS.PRODUCT_ID

ORDER BY PRODUCT.NAME;

Соответственно, сначала мы считаем все приходы, потом считаем все расходы по каждому товару и делаем разницу между количеством приходов и количеством расходов. Потом объединяем с товаром и смотрим сколько получилось. Естественно, этот запрос сильно упрощён, на практике остатки считаются обычно гораздо сложнее, но по производительности вы увидите разницу. То есть этот запрос на моём компьютере выполнялся 29 с небольшим секунд. 

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

Практика. Остатки товаров. Как улучшить?

Теперь подумаем, как можно это делать лучше с помощью материализованных представлений. Мы можем сделать так - будем считать, что в текущем квартале у нас остатки меняются довольно-таки часто, и мы их будем считать. А за предыдущие кварталы будем считать, что остатки уже не будут меняться, и поэтому мы можем их хранить в материализованном представлении. И поэтому мы можем остатки на начало текущего квартала брать из материализованного представления и суммировать их с остатками, которые получаются за текущий квартал, и выводить их пользователю.

SQL для вычисления остатков на конец пред. квартала

WITH

  T AS (

    SELECT S.PRODUCT_ID, SUM(QUANTITY) AS QUANTITY

    FROM

      PRODUCT_STORE S

    WHERE S.DATE_OF_RECEIPT < FIRST_DAY(OF QUARTER FROM CURRENT_DATE)

    GROUP BY 1

    UNION ALL

    SELECT L.PRODUCT_ID, -SUM(L.QUANTITY) AS QUANTITY

    FROM

      INVOICE_LINE L

      JOIN INVOICE ON INVOICE.INVOICE_ID = L.INVOICE_ID

    WHERE INVOICE.CREATE_DATE < FIRST_DAY(OF QUARTER FROM CURRENT_DATE)

    GROUP BY 1

  )

SELECT

  T.PRODUCT_ID, SUM(T.QUANTITY) AS QUANTITY

FROM T

GROUP BY 1;

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

MATERIALIZED VIEW для хранения остатков на конец пред. квартала

CREATE MATERIALIZED VIEW V_REMAIN_PRODUCTS

AS

WITH

  T AS (

    SELECT S.PRODUCT_ID, SUM(QUANTITY) AS QUANTITY

    FROM PRODUCT_STORE S

    WHERE S.DATE_OF_RECEIPT < FIRST_DAY(OF QUARTER FROM CURRENT_DATE)

    GROUP BY 1

    UNION ALL

    SELECT L.PRODUCT_ID, -SUM(L.QUANTITY) AS QUANTITY

    FROM

      INVOICE_LINE L

      JOIN INVOICE ON INVOICE.INVOICE_ID = L.INVOICE_ID

    WHERE INVOICE.CREATE_DATE < FIRST_DAY(OF QUARTER FROM CURRENT_DATE)

    GROUP BY 1

  )

SELECT

  T.PRODUCT_ID, SUM(T.QUANTITY) AS QUANTITY

FROM T

GROUP BY 1 

WITH DATA -- создание с данными, если WITH NO DATA (умолчание), то без данных

;

Теперь создаём материализованные представления на основе этого запроса. Просто записываем CREATE MATERIALIZED VIEW, и далее вот этот запрос. После запроса вы можете указать, создается он с данными, либо без данных, с помощью предложения WITH DATA, WITH NO DATA. 

Создание MATERIALIZED VIEW. СТАТИСТИКА

И здесь вот показаны две статистики. Если он без данных, естественно, там одни метаданные создаются, материализованные представления создаются там за 0,009 секунды, это очень быстро. А если с данными, то за 23 секунды, тут погрешность. Фактически с той же скоростью, как непосредственный запрос, который внутри материализованного представления. Соответственно, в статистике вы видите, что весь внутренний запрос был выполнен и 10 тысяч записей, то есть по каждому товару, было вставлено в материализованное представление.

MATERIALIZED VIEW для хранения остатков на конец пред. квартала

СОЗДАНИЕ ИНДЕКСА

CREATE UNIQUE INDEX IDX_V_REMAIN_PRODUCTS_PRODUCT_ID 

ON V_REMAIN_PRODUCTS(PRODUCT_ID);

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

SQL ЗАПРОС ДЛЯ ВЫЧИСЛЕНИЯ ТЕКУЩИХ ОСТАТКОВ

WITH

  T AS (

    SELECT S.PRODUCT_ID, SUM(S.QUANTITY) AS QUANTITY

    FROM PRODUCT_STORE S

    WHERE S.DATE_OF_RECEIPT >= FIRST_DAY(OF QUARTER FROM CURRENT_DATE)

    GROUP BY 1

    UNION ALL

    SELECT L.PRODUCT_ID, -SUM(L.QUANTITY) AS QUANTITY

    FROM

      INVOICE_LINE L

      JOIN INVOICE ON INVOICE.INVOICE_ID = L.INVOICE_ID

    WHERE INVOICE.CREATE_DATE >= FIRST_DAY(OF QUARTER FROM CURRENT_DATE)

    GROUP BY 1

  ),

  REMAIN_PRODUCTS AS (

    SELECT PRODUCT_ID, SUM(QUANTITY) AS QUANTITY

    FROM T 

    GROUP BY 1

    UNION ALL

    SELECT PRODUCT_ID, QUANTITY

    FROM V_REMAIN_PRODUCTS

  ),

  T2 AS (SELECT PRODUCT_ID, SUM(QUANTITY) AS QUANTITY FROM REMAIN_PRODUCTS GROUP BY 1)

SELECT

  T2.PRODUCT_ID, PRODUCT.NAME AS PRODUCT_NAME, T2.QUANTITY

FROM T2 JOIN PRODUCT ON PRODUCT.PRODUCT_ID = T2.PRODUCT_ID

ORDER BY PRODUCT.NAME;

Теперь переписываем запрос, так чтобы часть его считалась с помощью материализованного представления, а часть — текущий квартал. То есть вот здесь вот в первом табличном выражении вычисляются остатки товара за текущий квартал,а далее идёт объединение остатков из материализованного представления, получается, на конец предыдущего квартала, с остатками текущих кварталов. И далее просто мы их суммируем по каждому продукту, эти остатки и выводим на экран. С материализованным представлением скорость выполнения такого запроса, то есть полностью остатки по всем 10 тысячам позиций, выполнили за 0,3 секунды. 

Сравним производительность 29.398 sec vs 0.308 sec. Это ~ 95 раз быстрее.

Показана статистика,по каждой таблице уже читаем не все данные, а только те, которые у нас в текущем квартале и  плюс 10 тысяч записей читаем из материализованного представления. То есть если сравнить 29 секунд и 0,3 секунды, это примерно в 95 раз быстрее.

ЭКСКЛЮЗИВНОЕ ОБНОВЛЕНИЕ MATERIALIZED VIEW

Теперь попробуем как-то обновить данные в материализованном представлении. То есть, допустим, у нас в какой-то одной базовой таблице мы взяли и обновили 1168 записей. И теперь мы можем выполнить команду REFRESH MATERIALIZED VIEW, и она выполняется, опять же, со скоростью внутреннего запроса, который в материализованном представлении указан. 

То есть за 23 секунды вся эта команда выполняется. Тут важно учесть, что сколько бы вы данных на самом деле ни поменяли, команда REFRESH MATERIALIZED VIEW всегда перезальёт все данные,но сделает это достаточно быстро. Но эта команда требует эксклюзивного режима, то есть в это время, когда происходит обновление самого материализованного представления, с ним работать нельзя.

КОНКУРЕНТНОЕ ОБНОВЛЕНИЕ MATERIALIZED VIEW

Если необходимо конкурентное обновление, вы можете использовать REFRESH MATERIALIZED VIEW CONCURRENTLY. 

Тут тоже мы как бы вернули эти остатки назад или попробовали его обновить. Эта команда выполняется чуть дольше, потому что внутри  хоть это DDL-запрос, внутри выполняются некоторые DML-запросы, которые, собственно говоря, обновляют данные материализованного представления с помощью уникального индекса. Вот здесь показано, что мы же для одного продукта обновляли, а реальное обновление произошло одно. Но это выполнение немного дольше, чем полное обновление материализованного представления. Соответственно, чем больше данных вы поменяете в материализованном представлении, такое обновление может быть дольше. Зато оно позволяет работать с материализованным представлением, не выгоняя всех.

Если нужно удалить данные материализованного представления, собственно, выполняем команду REFRESH MATERIALIZED VIEW DROP DATA. Она выполняется, ну, тоже очень быстро, видите, за пять тысячных секунды, потому что реального DELETE там не выполняется, мусора никакого не будет, просто страницы помечаются свободными.

Вопросы?

Вопросы можете задать в техническую поддержку HQbird support@ibase.ru

Присоединяйтесь к сообществу СУБД FIrebird в Telegram и Max