Документация Postgres Pro AXE
1.0.0

Общество с ограниченной ответственностью «Постгрес Профессиональный»

Авторские права © 2016–2026 Постгрес Профессиональный

Юридическое уведомление

Представленная документация предназначена исключительно для использования с программным продуктом Postgres Pro AXE и для пользователей данного продукта.

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

Иные условия использования данной документации приведены в Пользовательском соглашении.

Postgres Pro © 2016–2026 — Общество с ограниченной ответственностью «Постгрес Профессиональный».

Общество с ограниченной ответственностью «Постгрес Профессиональный» (Postgres Professional) не несёт никакой ответственности за любые убытки, включая упущенную выгоду, возникшие в результате использования данного программного продукта и/или документации пользователем, даже если Общество с ограниченной ответственностью «Постгрес Профессиональный» было извещено о возможности возникновения таких убытков.

Общество с ограниченной ответственностью «Постгрес Профессиональный» (Postgres Professional) отказывается предоставлять любые явно выраженные или подразумеваемые гарантии, включая, но не ограничиваясь: подразумеваемые гарантии товарной пригодности и/или пригодности для определённой цели, или возникающие в результате обычной практики деловых операций или обычаев делового оборота. Данный программный продукт предоставляется на основе принципа «как есть» и Общество с ограниченной ответственностью «Постгрес Профессиональный» не обязано предоставлять сопровождение, поддержку, обновления, расширения или изменения данного программного продукта.

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

Документация программного продукта «СУБД Postgres Pro AXE» во всех версиях основана на:

Документации PostgreSQL, которая распространяется по следующей лицензии:

Система управления базами данных PostgreSQL (также известная как Postgres, ранее известная как Postgres95)

Частичные авторские права © 1996-2026 — PostgreSQL Global Development Group.

Частичные авторские права © 1994 — Регенты университета Калифорнии.

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

УНИВЕРСИТЕТ КАЛИФОРНИИ НИ В КОЕЙ МЕРЕ НЕ НЕСЁТ ОТВЕТСТВЕННОСТИ ЗА ПРЯМОЙ, КОСВЕННЫЙ, НАМЕРЕННЫЙ, СЛУЧАЙНЫЙ ИЛИ СПРОВОЦИРОВАННЫЙ УЩЕРБ, В ТОМ ЧИСЛЕ ПОТЕРЯННЫЕ ПРИБЫЛИ, СВЯЗАННЫЙ С ИСПОЛЬЗОВАНИЕМ ЭТОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ И ЕГО ДОКУМЕНТАЦИИ, ДАЖЕ ЕСЛИ УНИВЕРСИТЕТ КАЛИФОРНИИ БЫЛ УВЕДОМЛЁН О ВОЗМОЖНОСТИ ТАКОГО УЩЕРБА.

УНИВЕРСИТЕТ КАЛИФОРНИИ ЯВНЫМ ОБРАЗОМ ОТКАЗЫВАЕТСЯ ОТ ЛЮБЫХ ГАРАНТИЙ, В ЧАСТНОСТИ ПОДРАЗУМЕВАЕМЫХ ГАРАНТИЙ КОММЕРЧЕСКОЙ ВЫГОДЫ ИЛИ ПРИГОДНОСТИ ДЛЯ КАКОЙ-ЛИБО ЦЕЛИ. НАСТОЯЩЕЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ В ВИДЕ «КАК ЕСТЬ», И УНИВЕРСИТЕТ КАЛИФОРНИИ НЕ ДАЁТ НИКАКИХ ОБЯЗАТЕЛЬСТВ ПО ЕГО ОБСЛУЖИВАНИЮ, ПОДДЕРЖКЕ, ОБНОВЛЕНИЮ, УЛУЧШЕНИЮ ИЛИ МОДИФИКАЦИИ.

Документации DuckDB, которая распространяется по следующей лицензии:

Авторское право 2024-2025 Stichting DuckDB Foundation

Настоящим предоставляется бесплатно любому лицу, получающему копию данного программного обеспечения и связанных с ним файлов документации («Программное обеспечение»), на работу с Программным обеспечением без ограничений, включая, без ограничений, права на использование, копирование, модификацию, объединение, публикацию, распространение, сублицензирование и/или продажу копий Программного обеспечения и разрешать лицам, которым предоставляется Программное обеспечение, осуществлять указанные выше права при соблюдении следующих условий:

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

ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ КОММЕРЧЕСКОЙ ВЫГОДЫ ИЛИ ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ И НЕНАРУШЕНИЯ АВТОРСКИХ ПРАВ. НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ЗА КАКИЕ-ЛИБО ПРЕТЕНЗИИ, УЩЕРБ ИЛИ ИНУЮ ОТВЕТСТВЕННОСТЬ, БУДЬ ТО В РЕЗУЛЬТАТЕ ДЕЙСТВИЯ ДОГОВОРА, ГРАЖДАНСКОГО ПРАВОНАРУШЕНИЯ ИЛИ ИНЫМ ОБРАЗОМ ВЫТЕКАЮЩИЕ ИЗ-ЗА ИЛИ В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ, ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ДРУГИМИ ОПЕРАЦИЯМИ С НИМ.

Часть I. Обзор

Postgres Pro AXE — это аналитическая редакция СУБД Postgres Pro, которая позволяет обрабатывать большие объёмы нагрузок OLTP (online transaction processing, обработка транзакций в реальном времени), OLAP (online analytical processing, интерактивная аналитическая обработка) и HTAP (hybrid transactional and analytical processing, гибридная транзакционная и аналитическая обработка). Она предназначена для разработки и упрощения хранилищ данных (data warehouse, DWH) и архитектур LakeHouse, а также для создания гибриданых решений по обработке OLTP и OLAP без использования сложных ETL-операций.

Глава 1. Ключевые преимущества

Postgres Pro AXE предоставляет следующие ключевые преимущества:

Скорость и эффективность: аналитические запросы обрабатываются до 20 раз быстрее, чем Greenplum и до 30 раз быстрее, чем PostgreSQL согласно тестам производительности ClickBench и TCP-H/TCP-DS, выполненным на сложных запросах.

Быстрое старт: используется синтаксис PostgreSQL, поэтому развёртывания дополнительных систем и обучения сотрудников новым инструментам не требуется.

Экономия ресурсов:

не требуется интеграция с дополнительными хранилищами и ETL-серверами (Extract, Transform, Load);

используется до 10 раз меньше ресурсов ОЗУ и ЦП, чем Greenplum согласно тестам производительности.

Упрощённый стек разработки: можно построить полноценное хранилище данных (Data Warehouse, DWH), используя только компоненты Postgres Pro.

Хранение данных в открытых форматах, доступных для внешних инструментов, таких как BI-системы, панели мониторинга и другие приложения:

OLAP-данные аналитических таблиц хранятся как Parquet-файлы;

метаданные аналитических таблиц хранятся в SQL-таблицах в соответствии со спецификацией DuckLake.

Глава 2. Основные компоненты

Основными компонентами Postgres Pro AXE являются:

СУБД Postgres Pro.

pgpro_axe: векторно-столбцовый аналитический механизм, предоставляющий быстрые запросы к столбцовым источникам данных, например к Parquet-файлам и временным таблицам. Он практически полностью совместим с синтаксисом PostgreSQL и может работать с OLAP-данными из локальных, сетевых и S3-хранилищ. pgpro_axe также позволяет объединять данные из таблиц-куч Postgres Pro и столбцовых источников в рамках одного аналитического запроса.

Это расширение состоит из следующих компонентов:

проприетарные компоненты Postgres Pro AXE;

компоненты Postgres Pro;

DuckDB с её функциональностью.

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

Решение можно в любой момент масштабировать как вертикально, так и горизонтально.

Глава 3. Бизнес-сценарии

Использование Postgres Pro AXE в качестве хранилища аналитических данных позволяет реализовывать различные бизнес-сценарии, включая, но не ограничиваясь следующими сценариями:

Финансы и бюджетирование:

сравнение фактических и плановых показателей доходов и расходов (по месяцам и кварталам, а также между разными отделами);

анализ прибыльности продуктов, услуг и отделов;

контроль бюджетов и выявление отклонений.

Умное ценообразование и маркетинг:

динамическое ценообразование (анализ спроса, цен конкурентов и остатков на складе);

оценка эффективности каналов продаж, регионов и менеджеров;

анализ покупательского поведения (предпочитаемые товары, время и место покупок);

сегментация клиентов для целевых маркетинговых кампаний.

Служба поддержки:

выявление ключевых проблем и их причин;

мониторинг производительности (качество и скорость);

анализ удовлетворенности покупателей.

Безопасность:

анализ журналов (выявление аномалий и паттернов);

расследование инцидентов и поиск зависимых событий.

Управление складскими запасами и резервами:

анализ оборачиваемости запасов;

прогнозирование спроса и оптимизация закупок.

За подробной информацией о сценариях обработки OLAP-данных обратитесь к Главе 4.

Часть II. Развёртывание

Процесс развёртывания Postgres Pro AXE состоит из следующих шагов:

Определите сценарий обработки OLAP-данных и сценарий развёртывания.

Убедитесь, что ваши серверы соответствуют аппаратным и программным требованиям.

Выделите достаточное количество вычислительных ресурсов для выполнения аналитических запросов.

Определите группы пользователей и сценарии использования.

Организуйте ETL-операции.

Спланируйте масштабируемость вычислительных ресурсов.

Обеспечьте отказоустойчивость.

Установите Postgres Pro AXE.

Глава 4. Сценарии обработки OLAP-данных

Postgres Pro AXE предусматривает следующие сценарии обработки OLAP-данных, но не ограничивается ими:

Сокращение OLAP-нагрузки на OLTP-сервер и ускорение создания отчётов.

Обеспечение хранения истории изменений аналитических таблиц и поиск исторических данных в потоке событий инфраструктуры и безопасности.

Использование свободных мощностей резервных серверов для обработки OLAP-данных.

Перенос OLAP-нагрузки с перегруженного OLAP-кластера (например, Greenplum) на Postgres Pro AXE.

Создание полноценного хранилища данных.

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

Глава 5. Сценарии развёртывания

Postgres Pro AXE подразумевает следующие основные сценарии развёртывания, но не ограничивается ими:

Один сервер, обрабатывающий OLAP и OLTP-нагрузки одновременно без отказоустойчивости.

Кластер, в котором главный сервер обрабатывает OLTP-нагрузку, а один из резервных серверов обрабатывает OLAP-нагрузку.

Кластер, в котором главный сервер обрабатывает OLTP-нагрузку, а резервные серверы обрабатывают OLAP-нагрузку, сбалансированную proxima.

Parquet-файлы с OLAP-данными можно хранить в локальном, сетевом или S3-хранилище.

Во всех сценариях развёртывания расширения pgpro_axe и pgpro_metastore устанавливаются на каждом сервере, обрабатывающем OLAP-нагрузку.

5.1. Влияние аналитических запросов на OLTP-нагрузку

Риск влияния аналитических запросов на OLTP-нагрузку зависит от того, где они выполняются. Например, рассмотрим кластер, в котором главный сервер обрабатывает OLTP-нагрузку, а синхронный и асинхронный резервные серверы обрабатывают OLAP-нагрузку:

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

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

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

Рекомендации по обработке OLAP-нагрузки:

Используйте главный сервер и синхронные резервные серверы для обработки стабильной и проверенной OLAP-нагрузки в соответствии с доступными вычислительными ресурсами. При этом избегайте предоставления прямого доступа группам аналитиков, а также выполнения незапланированных (ad hoc) аналитических запросов, непроверенных аналитических запросов, а также аналитических запросов с непредсказуемым использованием ресурсов.

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

Используйте автономные серверы Postgres Pro, работающие с общим сетевым или S3-хранилищем, для создания, тестирования и выполнения незапланированных аналитических запросов с непредсказуемым временем выполнения.

5.2. Проверка доступных сетевых и вычислительных ресурсов, а также ресурсов хранения

При развёртывании Postgres Pro AXE распределяйте OLAP-нагрузку между серверами таким образом, чтобы получившаяся в результате топология отвечала требованиям к производительности.

Рекомендации по построению топологии:

Определите основные OLAP-процессы и приложения и установите временные ограничения на выполнение ETL-операций.

Подготовьте диаграмму перемещения данных между хранилищами.

Убедитесь, что сеть и хранилища обеспечивают достаточную пропускную способность для выполнения операций с рекомендуемым запасом в 30%.

Выберите серверы, которые будут использоваться для обработки большей части OLAP-нагрузки. Убедитесь, что эти узлы имеют достаточно вычислительных ресурсов для выполнения ETL-операций в рамках требуемых ограничений по времени с рекомендуемым запасом в 30%.

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

Глава 6. Аппаратные и программные требования

При развёртывании Postgres Pro AXE учитывайте следующие минимальные аппаратные требования:

Для установки СУБД Postgres Pro AXE:

ЦП с частотой 1 ГГц

ОЗУ: 1 ГБ

Дисковое пространство: 1 ГБ

Примечание

Postgres Pro AXE поддерживает использование Postgres Pro Enterprise 17.7.1 в качестве СУБД.

Для обработки OLAP-нагрузки:

4 ядра ЦП

ОЗУ: 16 ГБ

Дисковое пространство: 100 ГБ

Postgres Pro AXE не может функционировать на виртуальных машинах без векторных инструкций ЦП, в частности SSE4.2 и AVX512F. Перед установкой решения убедитесь, что инструкции установлены на целевых ВМ.

Пример 6.1.

Чтобы посмотреть доступные векторные инструкции ЦП в операционных системах, схожих с Debian, выполните следующую команду:

grep -Eow "sse4_2|avx|avx2|avx512f" /proc/cpuinfo | sort -u

В выводе команды должны быть все четыре инструкции.

Операционные системы, поддерживаемые для архитектуры x86-64:

Debian 13

Ubuntu 24.04

РЕД ОС 8

Astra Linux 1.8

Альт 11

При развёртывании Postgres Pro AXE с архитектурой NUMA (non-uniform memory access, неоднородный доступ к памяти), рекомендуется выделить:

8 ядер процессора на каждый узел NUMA;

как минимум 4 ГБ ОЗУ на каждое ядро процессора в узле NUMA.

Глава 7. Планирование ресурсов

При развёртывании Postgres Pro AXE необходимо выделить достаточное количество вычислительных ресурсов для выполнения аналитических запросов. Количество требуемых ресурсов зависит от следующих параметров хранилища:

Общий объём OLAP-данных в хранилище.

Объём данных, обрабатываемых стандартным аналитическим запросом.

Требуемое время выполнения аналитических запросов.

Количество аналитических запросов, выполненных за единицу времени.

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

После развёртывания Postgres Pro AXE выполняйте мониторинг доступных вычислительных ресурсов и будьте готовы при необходимости выделить дополнительные ресурсы за пределами этих рекомендаций.

7.1. Общий объём OLAP-данных в хранилище

Для аналитических таблиц необходимо выделить достаточное количество свободного места в локальном, сетевом или S3-хранилище. Как правило, Parquet-файлы хранятся в сжатом формате, например Snappy или zstd, и составляют около 20% от первичных неструктурированных текстовых данных или данных таблиц Postgres Pro.

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

7.2. Объём данных, обрабатываемых стандартным аналитическим запросом

Для эффективного выполнения некоторых аналитических запросов требуется большой объём оперативной памяти, в частности, для следующих запросов:

объединение двух больших аналитических таблиц;

агрегирование большого количество уникальных ключей;

сортировка больших аналитических таблиц.

Объём оперативной памяти, необходимый для конкретной OLAP-нагрузки, зависит от количества и размера аналитических запросов в экземпляре Postgres Pro AXE. Выполняйте мониторинг доступной оперативной памяти и при необходимости выделяйте дополнительную оперативную память.

Для изначального развёртывания выделите 4 ядра ЦП на каждого аналитика, а также около 6 ГБ оперативной памяти на каждое ядро ЦП, зарезервированное для выполнения аналитических запросов.

Каждые 24 часа выполняйте мониторинг количества временных файлов, создаваемых pgpro_axe, и избегайте пиковых значений. Временные файлы располагаются в каталоге, указанном параметром конфигурации duckdb.temporary_directory.

Если pgpro_duckdb не хватает оперативной памяти для выполнения аналитического запроса, связанные с ним OLAP-данные помещаются во временные файлы, что значительно увеличивает время выполнения такого запроса. Чтобы максимально увеличить общую пропускную способность кластера Postgres Pro AXE и количество аналитических запросов, выполняемых за единицу времени, обрабатывайте большую часть аналитических запросов в оперативной памяти.

Однако временные файлы можно использовать при обработке ресурсоёмких аналитических запросов с большим временем выполнения (например, несколько часов).

7.3. Требуемое время выполнения аналитических запросов

Некоторые аналитические запросы должны быть выполнены в течение ограниченного периода времени. В частности, это относится к интерактивным запросам, а также к запросам, результаты которых должны быть доступны к определённой дате и времени, например к началу рабочего дня. Если для выполнения аналитических запросов выделено достаточно оперативной памяти и для них не создаются временные файлы, время выполнения определяется количеством ядер ЦП, которые зарезервированы для выполнения аналитических запросов.

Для определения необходимого количества ядер ЦП используйте внутренние тесты производительности. В рамках таких тестов чувствительные ко времени аналитические запросы выполняются к тестовым данным. Размер и структура тестовых данных имитируют реальные данные, которые будут использованы.

7.4. Количество аналитических запросов, выполненных за единицу времени

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

Выполнение некоторых ETL-операций можно запланировать на конкретное время дня, в то время как для других задач, таких как выполнение незапланированных (ad hoc) аналитических запросов, это сделать невозможно.

Избегайте пиковых OLAP-нагрузок за счёт равномерного распределения выполнения аналитических запросов в течение дня, оставляя при этом запас в 30% для непредвиденных задач.

pgpro_axe обрабатывает аналитические запросы линейно, пока ему хватает ресурсов ввода-вывода, оперативной памяти, а также ЦП. При истощении этих ресурсов производительность падает.

Рекомендации по избежанию истощения ресурсов:

устраните узкие места в кластере Postgres Pro;

снизьте пиковые OLAP-нагрузки;

оптимизируйте выполнение аналитических запросов;

перенесите ETL-операции и незапланированные аналитические запросы на выделенный сервер Postgres Pro AXE, например на асинхронную резервную реплику.

7.5. Особенности и ограничения

Postgres Pro AXE имеет следующие ограничения, которые необходимо учитывать при выполнении OLAP-операций:

pgpro_axe выполняет SQL-запросы на одном сервере и ограничено мощностью сервера. Как правило, количество данных, которые pgpro_axe может обработать в одном SQL-запросе на стандартном сервере (256 ЦП, 2 ТБ оперативной памяти), не превышает 100 ТБ.

Чтобы обработать больший объём данных, разделите изначальные данные на несколько небольших частей. Обработайте каждую часть в отдельном аналитическом запросе и сохраните результат во временную таблицу. После обработки всех частей объедините результат в окончательный аналитический запрос.

Для массивно-параллельного режима с большим количеством задействованных серверов необходимо балансировать SQL-запросы на уровне приложений или использовать Proxima.

SQL-синтаксис pgpro_axe совместим с синтаксисом PostgreSQL, но не полностью повторяет его. Некоторые работающие в Postgres Pro запросы необходимо изменить, чтобы их можно было выполнить в pgpro_axe.

Глава 8. Определение групп пользователей и сценариев использования

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

Определите сценарии использования для групп пользователей.

Определите необходимую скорость отклика и настройте доступ к OLAP-данным для сценариев использования.

Postgres Pro AXE предоставляет следующие способы настройки доступа к OLAP-данным:

У каждой группы пользователей отдельный каталог pgpro_metastore и хранилище.

У групп пользователей общий каталог pgpro_metastore, но хранилище, где находится основная масса OLAP-данных, для каждой группы отдельное. Остальные OLAP-данные помещаются в общее хранилище, к которому имеют доступ все группы пользователей.

У групп пользователей общий каталог pgpro_metastore и хранилище.

Доступ к OLAP-данным можно настроить, используя один из описанных выше сценариев, или иным способом при помощи таблиц метаданных pga_storage, pga_table и pga_folder.

Глава 9. Организация ETL-операций

Определите, как OLAP-данные добавляются в хранилища, и организуйте ETL-операции для сценариев развёртывания.

Добавить OLAP-данные в хранилища можно одним из следующих способов:

Из аналитических таблиц или напрямую из Parquet-файлов, используя ETL-операции.

Это самый простой и надёжный способ, поскольку он обеспечивает изменения OLAP-данных и метаданных в рамках транзакций.

Использование SQL-скриптов и пользовательских ETL-операций.

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

Важно

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

Для выполнения сложной обработки исходных OLAP-данных в ходе ETL-операций можно использовать операции Postgres Pro, временные таблицы и пользовательские скрипты для преобразования данных.

Глава 10. Планирование масштабируемости вычислительных ресурсов

По сравнению с другими решениями Postgres Pro AXE требуется меньше вычислительных ресурсов для реализации сценариев обработки OLAP-данных. Ресурсы для обработки и хранения не зависят друг от друга, а при необходимости можно использовать дополнительные мощности.

Возможны следующие варианты топологии серверов Postgres Pro AXE:

Один сервер с 256 ядрами ЦП, 2 ТБ оперативной памяти и массивом NVMe-дисков.

Этого достаточно для обработки до 100 ТБ OLAP-данных аналитическими запросами.

Несколько серверов.

OLAP-нагрузка распределяется по серверам с помощью приложений и ETL-скриптов (Extract, Transform, Load — операции выгрузки, преобразования и загрузки).

Кластер конструкции главный-резервный.

OLAP-нагрузка распределяется по резервным серверам с помощью proxima.

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

Глава 11. Обеспечение отказоустойчивости

Резервные копии OLAP-данных можно создавать в локальных хранилищах с помощью BiHA и pg_probackup. В сетевых и S3-хранилищах используйте BiHA и pg_probackup для данных СУБД Postgres Pro AXE, а также инструменты резервного копирования поставщика хранилища для OLAP-данных.

Реализация отказоустойчивости зависит от времени восстановления OLAP-данных (обратитесь к таблице ниже).

Время восстановления OLAP-данных

Реализация

Дни

Полное резервное копирование OLAP-данных

Часы, минуты

Инкрементальное резервное копирование OLAP-данных

Секунды

Полное резервное копирование OLAP-данных на резервных серверах и отказоустойчивое сетевое или S3-хранилище

Примечание

По сравнению со сценариями обработки OLTP-данных и анализом данных в режиме реального времени, большинство сценариев обработки OLAP-данных не имеют строгих требований ко времени восстановления.

Глава 12. Установка Postgres Pro AXE

На серверах, подготовленных для вашего сценария развёртывания:

Установите СУБД Postgres Pro AXE из дистрибутива или другую поддерживаемую СУБД.

За подробной информацией обратитесь в техническую поддержку Postgres Pro.

Установите pgpro_axe и pgpro_metastore:

Сначала установите пакет pgpro_axe, а затем пакет pgpro_metastore, полученные от Postgres Pro.

В файле конфигурации postgresql.conf укажите pgpro_axe, pgpro_metastore в качестве значений для shared_preload_libraries.

Важно

Укажите имена расширений строго в таком порядке. В противном случае расширения не будут установлены.

Перезапустите сервер.

Сначала создайте pgpro_axe, а затем pgpro_metastore в базе данных сервера:

CREATE EXTENSION pgpro_axe;
CREATE EXTENSION pgpro_metastore;

Убедитесь, что Postgres Pro AXE работает правильно. Для этого создайте Parquet-файлы в хранилище:

Пример 12.1. Создание Parquet-файлов в локальном или сетевом хранилище

COPY (
SELECT test_id, test_name
FROM tbl_test
WHERE test_id between 22 and 30
) TO '/tmp/data_parquet/test_local.parquet' (FORMAT parquet, COMPRESSION zstd);

Пример 12.2. Создание Parquet-файлов в S3-хранилище

user@db=# COPY (
SELECT test_id, test_name
FROM tbl_test
WHERE test_id between 1 and 10
) TO 's3://axedemobucket/test01.parquet' (FORMAT parquet, COMPRESSION zstd);
COPY 10
user@db=# COPY (
SELECT test_id, test_name
FROM tbl_test
WHERE test_id between 11 and 20
) TO 's3://axedemobucket/test02.parquet' (FORMAT parquet, COMPRESSION zstd);
COPY 10

Если Parquet-файлы созданы успешно, для них можно выполнять выборку:

Пример 12.3. Выборка Parquet-файлов из локального или сетевого хранилища

SELECT r['column_name'] FROM read_parquet('/path/test.parquet') r;

Пример 12.4. Выборка Parquet-файлов из S3-хранилища

SELECT r['column_name'] FROM read_parquet('s3://bucket_name/test.parquet') r

Если вы можете создавать и выполнять выборку Parquet-файлов, Postgres Pro AXE установлен успешно.

Глава 13. Обновление и удаление Postgres Pro AXE

13.1. Обновление Postgres Pro AXE

Обновите СУБД Postgres Pro AXE.

За подробной информацией обратитесь в техническую поддержку Postgres Pro.

Удалите pgpro_axe и pgpro_metastore.

За подробной информацией обратитесь к официальной документации Postgres Pro по удалению расширений.

Установите pgpro_axe и pgpro_metastore из последнего дистрибутива, полученного от Postgres Pro.

За подробной информацией обратитесь к Главе 12.

13.2. Удаление Postgres Pro AXE

Важно

После удаления Postgres Pro AXE безвозвратно теряются OLTP и OLAP-данные, которые обрабатывались решением.

Чтобы удалить Postgres Pro AXE:

Удалите pgpro_axe и pgpro_metastore.

За подробной информацией обратитесь к официальной документации Postgres Pro по удалению расширений.

Удалите СУБД Postgres Pro AXE.

За подробной информацией обратитесь в техническую поддержку Postgres Pro.

Часть III. Администрирование

Глава 14. Управление доступом

Поскольку pgpro_metastore работает с собственными объектами, для разграничения прав и гибкого управления доступом к таким объектам используется система на основе прокси-таблиц.

Прокси-таблицы — это пустые таблицы без столбцов, созданные в схеме pgpro_metastore. Имя каждой прокси-таблицы соответствует формату префикс_имя, где:

префикс: префикс, определяющий объект, обёрнутый в прокси-таблицу.

Возможные значения:

s — для хранилищ;

t — для аналитических таблиц;

f — для каталогов.

имя: имя объекта, обёрнутого в прокси-таблицу.

Например, именем прокси-таблицы может быть s_localstorage.

Прокси-таблицы создаются и удаляются одновременно с объектами, которые они оборачивают.

За подробной информацией о настройке доступа к СУБД Postgres Pro AXE обратитесь к официальной документации Postgres Pro.

14.1. Настройка роли администратора

В Postgres Pro AXE роль администратора называется metastore_admin. Она может взаимодействовать с pgpro_axe, выполнять ETL-операции, а также предоставлять другим пользователям права на управление объектами.

Чтобы настроить роль администратора:

Создайте роль metastore_admin:

ALTER SYSTEM SET duckdb.postgres_role TO 'metastore_admin';

Предоставьте роли metastore_admin полный доступ к схеме pgpro_metastore и перезагрузите сервер pgpro_metastore.

14.2. Сценарий управления правами

Рассмотрим сценарий управления правами, в котором они предоставляются по иерархии: от главного администратора локальным администраторам групп пользователей и аналитикам (обратитесь к диаграмме ниже).

Где:

Суперпользователь — это суперпользователь Postgres Pro с полными правами.

metastore_admin — главный администратор с ролью metastore_admin, который является владельцем всех объектов pgpro_metastore.

Администраторы A и B — это локальные администраторы групп пользователей, у которых есть доступ к объектам pgpro_metastore, связанным с этими группами.

Права этим администраторам предоставляет главный администратор. Локальные администраторы в свою очередь могут предоставлять права аналитикам в рамках своих групп.

Аналитики A, B, C и D — пользователи, которые работают с OLAP-данными.

Чтобы настроить этот сценарий управления правами:

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

postgres=# SELECT current_user;
current_user
--------------
root
postgres=# CREATE USER metastore_admin;
postgres=# CREATE USER admin_a;
postgres=# CREATE USER admin_b;
postgres=# CREATE USER analyst_a;
postgres=# CREATE USER analyst_b;
postgres=# CREATE USER analyst_c;
postgres=# CREATE USER analyst_d;

Создайте аналитические схемы для групп пользователей:

postgres=# SELECT current_user;
current_user
--------------
root
postgres=# CREATE SCHEMA schema_a;
postgres=# CREATE SCHEMA schema_b;

Предоставьте пользователю admin_a права на аналитическую схему schema_a:

postgres=# SELECT current_user;
current_user
--------------
root
postgres=# GRANT ALL ON SCHEMA schema_a to metastore_admin WITH GRANT OPTION;
postgres=# GRANT ALL ON SCHEMA schema_a to admin_a WITH GRANT OPTION;

Предоставьте пользователю metastore_admin права на аналитическую схему metastore и сделайте его администратором:

postgres=# SELECT current_user;
current_user
--------------
root
postgres=# GRANT ALL ON SCHEMA metastore TO metastore_admin ;
postgres=# ALTER SYSTEM SET duckdb.postgres_role TO 'metastore_admin';

Перезапустите сервер.

Теперь пользователь metastore_admin может настроить каталог pgpro_metastore:

postgres=> SELECT current_user;
  current_user
-----------------
metastore_admin
postgres=> SELECT metastore.define_catalog_connection('localhost','5433','postgres','','');
postgres=> SELECT metastore.init();

Предположим, в схеме группы A есть таблица Postgres Pro, которую для выполнения аналитических запросов необходимо перенести в pgpro_metastore:

Пользователь metastore_admin создаёт хранилище и аналитическую таблицу:

postgres=> SELECT current_user;
  current_user
-----------------
metastore_admin
postgres=> SELECT metastore.add_storage('mt_storage', 'file:///tmp/mt_storage/', 'file:///tmp/mt_storage/tmp_dir/');
postgres=> SELECT metastore.add_table('mt_table1', 'mt_storage', 'schema_a.pg_table');

Пользователь metastore_admin находит имя прокси-таблицы, оборачивающей созданную аналитическую таблицу, выполнив запрос в базе данных, которая содержит каталог pgpro_metastore, т.е. схему axe_catalog:

SELECT proxy_table_name FROM axe_catalog.pga_proxy_table p
JOIN axe_catalog.pga_table t ON p.proxy_table_id = t.proxy_table_id
WHERE t.table_name = 'mt_table1';
 proxy_table_name
-------------------
 t_mt_table1_00001
(1 row)

Дальнейшие шаги выполняются на сервере, где установлено расширение pgpro_metastore.

Пользователь metastore_admin предоставляет права пользователю admin_a:

postgres=> SELECT current_user;
  current_user
-----------------
metastore_admin
postgres=> GRANT ALL ON metastore.t_mt_table1_00001 TO admin_a WITH GRANT OPTION;

Параметр WITH GRANT OPTION позволяет пользователю admin_a при необходимости предоставлять права другим пользователям.

Пользователь admin_a копирует таблицу Postgres Pro в аналитическую таблицу и создаёт представление Postgres Pro:

postgres=> SELECT current_user;
current_user
--------------
admin_a
postgres=> SELECT metastore.copy_table('mt_table1', 'select * from schema_a.pg_table');
postgres=> SELECT metastore.create_view('mt_table1', 'schema_a');

Заметьте, что пользователь, вызывающий функцию copy_table должен иметь достаточные права для выполнения запроса, переданного как параметр; в данном случае, admin_a должен иметь разрешение на SELECT из таблицы schema_a.pg_table.

Как владелец объекта пользователь metastore_admin разрешает пользователю admin_a предоставлять права на чтение этого объекта другим пользователям:

postgres=> SELECT current_user;
  current_user
-----------------
metastore_admin
postgres=> GRANT SELECT ON schema_a.mt_table1 TO admin_a WITH GRANT OPTION ;

Пользователь admin_a предоставляет аналитикам права на чтение представления Postgres Pro:

postgres=> SELECT current_user;
current_user
--------------
admin_a
(1 row)

postgres=> GRANT USAGE ON SCHEMA schema_a to analyst_a;
postgres=> GRANT SELECT ON schema_a.mt_table1 TO analyst_a;

Аналитик читает аналитическую таблицу:

postgres=> SELECT current_user;
  current_user
  --------------
  analyst_a
  (1 row)

  postgres=> SELECT count(*) FROM schema_a.mt_table1 ;
  count
  -------
      50
  (1 row)

14.3. ETL-операции и права на них

В этом разделе описываются права, необходимые для выполнения доступных ETL-операций.

cleanup

Только пользователь metastore_admin.

define_catalog_connection()

Только пользователь metastore_admin.

Эта операция является SECURITY DEFINER (определяющей контекст безопасности) и может выполняться только суперпользователем. Она может потребовать изменения в системном каталоге СУБД Postgres Pro AXE, к которому у пользователя metastore_admin нет права доступа.

init()

Только пользователь metastore_admin.

add_storage/remove_storage

Только пользователь metastore_admin.

Когда выполняется одна из этих ETL-операций, прокси-таблица, которой владеет пользователь metastore_admin и которая относится к хранилищу, создаётся в схеме pgpro_metastore или удаляется из неё.

add_table/remove_table

Только пользователь metastore_admin.

Когда выполняется одна из этих ETL-операций, прокси-таблица, которой владеет пользователь metastore_admin и которая относится к таблице, создаётся в схеме pgpro_metastore или удаляется из неё.

add_folder/remove_folder

Только пользователь metastore_admin.

Когда выполняется одна из этих ETL-операций, прокси-таблица, которой владеет пользователь metastore_admin и которая относится к общему каталогу, создаётся в схеме pgpro_metastore или удаляется из неё.

add_files

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

выполнять вставку в таблицу метаданных pga_table;

читать таблицу метаданных pga_folder, если Parquet-файлы добавляются из общего каталога, указанного в этой таблице.

copy_table

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

выполнять вставку в таблицу метаданных pga_table;

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

Эта ETL-операция является SECURITY DEFINER (определяющей контекст безопасности).

create_view

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

читать таблицу метаданных pga_table;

создавать представления в схеме Postgres Pro.

Эта ETL-операция является SECURITY DEFINER (определяющей контекст безопасности).

14.4. Примеры использования

Пример 14.1.

Чтобы создать роль mt_admin, выполните следующий запрос:

CREATE USER mt_admin;
GRANT ALL ON SCHEMA metastore TO mt_admin;
ALTER SYSTEM SET duckdb.postgres_role TO 'mt_admin';

Затем перезагрузите сервер.

Пример 14.2.

Чтобы предоставить пользователю user1 право для добавления OLAP-данных в аналитическую таблицу mt_table1 из общего каталога mt_storage, выполните следующий запрос:

postgres=# GRANT INSERT ON metastore.t_mt_table1_00001 TO user1;
postgres=# GRANT SELECT ON metastore.f_mt_folder to user1;

14.5. Секреты

Секреты DuckDB можно настроить с помощью вспомогательных функций или обёртки сторонних данных для более продвинутых сценариев.

Секреты хранятся в виде комбинаций SERVER и USER MAPPING в обёртке сторонних данных DuckDB. В USER MAPPING размещаются конфиденциальные элементы, такие как token, session_token и secret. Каждый раз, когда pgpro_axe создаёт экземпляр DuckDB, а также при изменении секрета, секреты загружаются в менеджер секретов DuckDB как непостоянные.

Важно

Не предоставляйте право USAGE для обёртки сторонних данных duckdb обычным пользователям.

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

Postgres Pro AXE поддерживает следующие типы секретов:

простые секреты;

секреты с поставщиком credential_chain;

секреты с secret_access_key.

Секретами можно управлять с помощью функций pgpro_axe.

14.5.1. Простые секреты

Настроить учётные данные проще всего с использованием вспомогательных функций:

Пример 14.3.

  -- Базовый секрет S3 (наиболее распространённый)
  SELECT duckdb.create_simple_secret(
      type := 'S3',
      key_id := 'your_access_key_id',
      secret := 'your_secret_access_key',
      region := 'us-east-1'
  );

У этой функции есть другие параметры:

Пример 14.4.

  SELECT duckdb.create_simple_secret(
      type          := 'S3',          -- Тип: один из (S3, GCS, R2)
      key_id        := 'идентификатор_ключа_доступа',
      secret        := 'xxx',
      session_token := 'yyy',         -- (необязательно)
      region        := 'us-east-1',   -- (необязательно)
      url_style     := 'xxx',         -- (необязательно)
      provider      := 'xxx',         -- (необязательно)
      endpoint      := 'xxx',         -- (необязательно)
      scope         := 'xxx',         -- (необязательно)
      validation    := 'xxx',         -- (необязательно)
      use_ssl       := 'xxx'          -- (необязательно)
  )

14.5.2. Секреты с поставщиком credential_chain

Для более продвинутых сценариев можно определять секреты с SERVER и USER MAPPING в обёртке сторонних данных DuckDB:

Пример 14.5.

    CREATE SERVER my_s3_secret
    TYPE 's3'
    FOREIGN DATA WRAPPER duckdb
    OPTIONS (PROVIDER 'credential_chain');

14.5.3. Секреты с secret_access_key

Если секрет содержит конфиденциальную информацию, необходимо создать дополнительный USER MAPPING:

Пример 14.6.

    CREATE SERVER my_s3_secret TYPE 's3' FOREIGN DATA WRAPPER duckdb;

    CREATE USER MAPPING FOR CURRENT_USER SERVER my_s3_secret
    OPTIONS (KEY_ID 'my_secret_key', SECRET 'my_secret_value');

Поддерживаемые DuckDB типы секретов можно использовать, если установлено соответствующее расширение. За более подробной информацией обратитесь к официальной документации DuckDB.

Глава 15. Хранилища

Хранилища — это физические расположения Parquet-файлов и общих каталогов. Postgres Pro AXE поддерживает следующие типы хранилищ:

Локальные хранилища: массивы дисков, поддерживающих протокол NVME (энергонезависимая память Express), на серверах с установленным Postgres Pro AXE.

Сетевые хранилища: сетевые файловые системы (Network File Systems, NFS).

S3-хранилища.

Чтобы определиться с типом хранилища, используйте информацию из таблицы ниже.

Локальное хранилище

Сетевое хранилище

S3-хранилище

Пропускная способность

Высокая

Определяется количеством NVME-дисков в массиве

Средняя

Ограничена возможностями сетевого интерфейса сервера, сетевой нагрузкой и скоростью сетевого хранилища

Средняя

Ограничена возможностями сетевого интерфейса сервера, сетевой нагрузкой и скоростью S3-хранилища

Масштабируемость данных

Средняя

Определяется количеством NVME-дисков и их объёмом

Высокая

Высокая

Распределение данных по серверам

Не поддерживается

Поддерживается в рамках сети организации

Глобальный

Поставщик отказоустойчивости

Администратор сервера Postgres Pro AXE

Администратор NFS

Поставщик услуг S3-хранилища

Стоимость за терабайт и за одно обращение к хранилищу

Низкая

Средняя

Зависит от поставщика услуг S3-хранилища

Метаданные хранилищ хранятся в таблице метаданных pga_storage.

Для хранения OLAP-данных можно использовать любую структуру каталогов, например:

В локальном или сетевом хранилище:

корневой_путь/имя_бд/имя_схемы/имя_таблицы

В S3-хранилище:

  s3://корзина/имя_бд/имя_схемы/имя_таблицы

pgpro_axe позволяет автоматически экспортировать OLAP-данные в несколько Parquet-файлов и добавлять уникальный номер к имени каждого файла. OLAP-данные проще хранить в виде нескольких Parquet-файлов одинакового размера.

Для организации OLAP-данных по ключам секционирования в иерархии каталогов можно также использовать hive-секционирование:

  table_name
  ├── year=2024
  │    ├── month=1
  │    │   ├── file1.parquet
  │    │   └── file2.parquet
  │    └── month=2
  │        └── file3.parquet
  └── year=2025
      ├── month=11
      │   ├── file4.parquet
      │   └── file5.parquet
      └── month=12
          └── file6.parquet

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

Перенос фильтров на уровень пути также поддерживается. Благодаря этому в процессе чтения можно пропустить пути, которые не содержат необходимых OLAP-данных.

Для работы с S3-хранилищами используются сторонние серверы Postgres Pro. Благодаря этому параметры подключения и учётные данные пользователей надёжно хранятся в Postgres Pro без их указания в функциях. В настоящее время можно создать только одно S3-хранилище.

15.1. Создание хранилища

Если вы планируете создать S3-хранилище, перед выполнением этой инструкции создайте простой секрет.

Чтобы создать хранилище, выполните следующий запрос:

  SELECT metastore.add_storage('имя_хранилища', 'URI_корневого_каталога', 'URI_временного_каталога');

Где:

имя_хранилища: уникальное имя хранилища.

URI_корневого_каталога: URI корневого каталога хранилища.

URI_временного_каталога: URI каталога хранилища для временных файлов.

У локальных хранилищ URI простой, например, file:///home/j.doe/workspace/axe/.

S3-хранилища отличаются более сложной структурой URI и логикой разбора, например, s3://premdb/team01.csv?versionId=w_B5qT8s5MkiT09.IRHay0lW.PycsHTS. Эти URI генерируются с помощью сторонних библиотек.

Пример 15.1. Создание локального хранилища

  SELECT metastore.add_storage('local_storage_example', 'file:///tmp/local_storage_example/', 'file:///tmp/local_storage_example/tmp_dir/');

Пример 15.2. Создание S3-хранилища

  SELECT metastore.add_storage('s3_storage_example', 's3://demo/s3_storage_example/','s3://demo/s3_storage_example/tmp/');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Проверяет, что для хранилища достаточно дискового пространства, по умолчанию требуется 10 ГБ.

Проверяет, что указанные каталоги существуют.

Создаёт новую запись в таблице метаданных pga_storage.

За подробной информацией о подключении к S3-хранилищу обратитесь к Приложению D.

15.2. Удаление хранилища

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

  SELECT metastore.remove_storage('имя_хранилища');

Где имя_хранилища — это имя хранилища, которое будет удалено.

Пример 15.3.

  SELECT metastore.remove_storage('storage_example');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot и указывает для хранилища и всех связанных с ним объектов pgpro_metastore значение end_snapshot в таблице метаданных pga_storage и других таблицах метаданных.

Глава 16. Аналитические таблицы

Аналитические таблицы — это наборы столбцов и строк с OLAP-данными, предоставляющие полную историю изменений данных и типа таблицы.

Строки аналитических таблиц хранятся в виде Parquet-файлов в хранилище. Метаданные аналитических таблиц хранятся в таблице метаданных pga_table.

16.1. Создание аналитической таблицы

16.1.1. Создание аналитической таблицы из Parquet-файла

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

  SELECT metastore.add_table('имя_таблицы', 'имя_хранилища', 'URI_Parquet-файла', '');

Где:

имя_таблицы: уникальное имя аналитической таблицы.

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

имя_хранилища: имя хранилища, в котором располагаются Parquet-файлы.

URI_Parquet-файла: URI Parquet-файла, из которого создаётся аналитическая таблица.

URI должен начинаться с file:// или s3://.

Важно

Не пропускайте последний пустой параметр — ''. Он зарезервирован для использования в будущем и без него запрос не работает.

Пример 16.1.

  SELECT metastore.add_table('table_example', 'storage_example', 'file:///home/user/tmp/parquet_data/t.parquet', '');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Убедитесь, что источник метаданных аналитической таблицы существует.

Использует библиотеку Apache Arrow для извлечения информации о столбцах из Parquet-файла.

Устанавливает соответствие между типом данных каждого столбца аналитической таблицы и типом данных, поддерживаемым pgpro_metastore.

Создаёт новые записи в таблицах метаданных pga_snapshot, pga_table и pga_column.

16.1.2. Создание аналитической таблицы из таблицы-кучи

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

  SELECT metastore.add_table('имя_таблицы', 'имя_хранилища', 'имя_таблицы-кучи', '');

Где:

имя_таблицы: уникальное имя аналитической таблицы.

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

имя_хранилища: имя хранилища, в котором располагаются Parquet-файлы.

имя_таблицы-кучи: полностью заданное имя таблицы-кучи, начинающееся с имени схемы.

Важно

Не пропускайте последний пустой параметр — ''. Он зарезервирован для использования в будущем и без него запрос не работает.

Пример 16.2.

  SELECT metastore.add_table('table_example', 'storage_example', 'public.heap_table_example', '');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Убедитесь, что источник метаданных аналитической таблицы существует.

Информация о столбцах аналитической таблицы извлекается из таблицы-кучи через запросы к служебным таблицам Postgres Pro.

Устанавливает соответствие между типом данных каждого столбца и типом данных, поддерживаемым pgpro_metastore.

Создаёт новые записи в таблицах метаданных pga_snapshot, pga_table и pga_column.

16.2. Переименование аналитической таблицы

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

  SELECT metastore.rename_table('имя_схемы.текущее_имя_таблицы' или 'текущее_имя_таблицы', 'новое_имя_таблицы');

Где:

имя_таблицы: имя аналитической схемы, в которой создана таблица.

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

текущее_имя_таблицы: текущее имя аналитической таблицы.

новое_имя_таблицы: новое уникальное имя аналитической таблицы.

Пример 16.3.

  SELECT metastore.rename_table('testtable', 'testtable1');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Обновляет значение table_name для аналитической таблицы в таблице метаданных pga_table.

Обновляет значение view_sql для аналитической таблицы.

16.3. Создание представления Postgres Pro из аналитической таблицы

Важно

Повторное создание представлений Postgres Pro возможно только в периоды отсутствия пользовательской активности. Это сделано для предотвращения потери данных.

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

  SELECT metastore.create_view('имя_таблицы', 'имя_схемы_Postgres_Pro');

Где:

имя_таблицы: имя аналитической таблицы, из которой создаётся представление Postgres Pro.

имя_схемы_Postgres_Pro: имя схемы Postgres Pro, связанной с представлением.

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

Пример 16.4.

  SELECT metastore.create_view('table_example');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Извлекает метаданные аналитической таблицы и путь к общему каталогу.

Генерирует запрос CREATE VIEW, в котором источником данных является результат вызова функции read_parquet().

Выполняет запрос CREATE VIEW.

Предоставляет права на представление Postgres Pro.

Создаёт новую запись в таблице метаданных pga_snapshot.

16.4. Удаление аналитической таблицы

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

  SELECT metastore.remove_table('имя_таблицы');

Где имя_таблицы — это имя аналитической таблицы, которая будет удалена.

Пример 16.5.

  SELECT metastore.remove_table('table_example');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot и указывает для аналитической таблицы и всех связанных с ней объектов pgpro_metastore значение end_snapshot в таблице метаданных pga_table и других таблицах метаданных.

Глава 17. Столбцы аналитических таблиц

За подробной информацией об аналитических таблицах обратитесь к Главе 16.

17.1. Создание столбца

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

  SELECT metastore.add_column('имя_схемы.имя_таблицы' или 'имя_таблицы', 'имя_столбца', 'тип_данных');

Где:

имя_таблицы: имя аналитической схемы, в которой создана таблица.

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

имя_таблицы: имя аналитической таблицы, в которой создан столбец.

имя_столбца: уникальное имя столбца.

тип_данных: тип данных столбца.

За подробной информацией о поддерживаемых типах данных обратитесь к Разделу G.3.

Пример 17.1.

  SELECT metastore.add_column('testtable', 'third_col', 'uint8');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot.

Обновляет значение view_sql для аналитической таблицы.

17.2. Переименование столбца

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

  SELECT metastore.rename_column('имя_схемы.имя_таблицы' или 'имя_таблицы', 'текущее_имя_столбца', 'новое_имя_столбца');

Где:

имя_таблицы: имя аналитической схемы, в которой создана таблица.

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

имя_таблицы: имя аналитической таблицы, в которой создан столбец.

текущее_имя_столбца: текущее имя столбца.

новое_имя_столбца: новое уникальное имя столбца.

Пример 17.2.

  SELECT metastore.rename_column('testtable', 'first_col', 'eleventh_col');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Обновляет значение column_name для столбца в таблице метаданных pga_column.

Обновляет значение view_sql для аналитической таблицы.

17.3. Изменение типа данных столбца

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

  SELECT metastore.change_column_type('имя_схемы.имя_таблицы' или 'имя_таблицы', 'имя_столбца', 'новый_тип_данных');

Где:

имя_таблицы: имя аналитической схемы, в которой создана таблица.

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

имя_таблицы: имя аналитической таблицы, в которой создан столбец.

имя_столбца: имя столбца, тип данных которых изменяется.

тип_данных: новый тип данных столбца.

За подробной информацией о поддерживаемых типах данных обратитесь к Разделу G.3.

Пример 17.3.

  SELECT metastore.change_column_type('testtable', 'third_col', 'uint8');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Проверяет, что новый тип данных столбца не совпадает с предыдущим.

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

int8 можно изменить на int16, int32 или int64;

int16 можно изменить на int32 или int64;

int32 можно изменить на int64;

uint8 можно изменить на uint16, uint32 или uint64;

uint16 можно изменить на uint32 или uint64;

uint32 можно изменить на uint64;

float32 можно изменить на float64.

Обновляет значение column_type для столбца в таблице метаданных pga_column.

Обновляет значение view_sql для аналитической таблицы.

17.4. Удаление столбца

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

  SELECT metastore.delete_column('имя_схемы.имя_таблицы' или 'имя_таблицы', 'имя_столбца');

Где:

имя_таблицы: имя аналитической схемы, в которой создана таблица.

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

имя_таблицы: имя аналитической таблицы, в которой создан столбец.

имя_столбца: имя удаляемого столбца.

Пример 17.4.

  SELECT metastore.delete_column('my_schema.testtable', 'second_col');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot и указывает для столбца значение end_snapshot в таблице метаданных pga_column.

Обновляет значение view_sql для аналитической таблицы.

Глава 18. Аналитические схемы

Аналитические схемы — это отдельные пространства имён для метаданных аналитических таблиц. Эти схемы похожи на схемы Postgres Pro.

Метаданные аналитических схем хранятся в таблице метаданных pga_schema.

18.1. Создание аналитической схемы

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

  SELECT metastore.add_schema('имя_схемы');

Где имя_схемы — это уникальное имя аналитической схемы.

Пример 18.1.

  SELECT metastore.add_schema('schema_example');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot.

18.2. Удаление аналитической схемы

Важно

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

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

  SELECT metastore.remove_schema('имя_схемы');

Где имя_схемы — это имя удаляемой аналитической схемы.

Пример 18.2.

  SELECT metastore.remove_schema('schema_example');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot и указывает для аналитической таблицы и всех связанных с ней объектов pgpro_metastore значение end_snapshot в таблице метаданных pga_schema и других таблицах метаданных.

Глава 19. Общие каталоги

Общие каталоги — это каталоги хранилища, в которых располагаются Parquet-файлы при добавлении или экспорте OLAP-данных из аналитических таблиц.

Метаданные общих каталогов хранятся в таблице метаданных pga_folder.

19.1. Создание общего каталога

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

  SELECT metastore.add_folder('имя_общего_каталога', 'имя_хранилища', 'путь_к_общему_каталогу');

Где:

имя_общего_каталога: уникальное имя общего каталога.

имя_хранилища: имя хранилища, в котором создаётся общий каталог.

путь_к_общему_каталогу: путь к общему каталогу относительно корневого каталога хранилища.

Пример 19.1.

  SELECT metastore.add_folder('shared_directory_example', 'storage_example', '/relative_path');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Проверяет, что указанный путь ведёт к существующему общему каталогу.

Создаёт новую запись в таблице метаданных pga_snapshot.

19.2. Удаление общего каталога

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

  SELECT metastore.remove_folder('имя_общего_каталога');

Где имя_общего_каталога — это имя удаляемого общего каталога.

Пример 19.2.

  SELECT metastore.remove_folder('shared_directory_example');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot и указывает для общего каталога значение end_snapshot в таблице метаданных pga_folder.

Глава 20. OLAP-данные

20.1. Добавление OLAP-данных в аналитическую таблицу из Parquet-файлов

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

  SELECT metastore.add_files('имя_таблицы', 'путь_к_файлам_Parquet');

Где:

имя_таблицы: имя аналитической таблицы, в которую добавляются OLAP-данные.

путь_к_Parquet-файлам: путь к Parquet-файлам или к общему каталогу с Parquet-файлами, из которых добавляются OLAP-данные.

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

Метаданные Parquet-файлов и аналитической таблицы должны быть совместимыми.

Пример 20.1.

  SELECT add_files('table_example', 'folder/file.parquet');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Проверяет, что метаданные Parquet-файлов и аналитической таблицы совместимы.

Копирует Parquet-файлы в общий каталог с Parquet-файлами аналитической таблицы.

Создаёт новые записи в таблицах метаданных pga_snapshot и pga_data_file.

Обновляет статистику по аналитической таблице и столбцам.

20.2. Копирование OLAP-данных в аналитическую таблицу из других таблиц

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

  SELECT metastore.copy_table('имя_таблицы', SQL-запрос);

Где:

имя_таблицы: имя аналитической таблицы, в которую копируются OLAP-данные.

SQL-запрос: SQL-запрос для определения копируемых OLAP-данных, например, SELECT * FROM my_pgtable.

SQL-запрос должен представлять собой одну команду SELECT. Операция UNION не поддерживается.

Пример 20.2.

  SELECT copy_table('table_example', $$select * from pg_table_example$$);

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Проверяет права пользователей.

Создаёт новую запись в таблице метаданных pga_snapshot.

Создаёт каталог для результатов SQL-запроса, которые сохраняются в виде Parquet-файлов.

Выполняет SQL-запрос и записывает его результаты в Parquet-файлы.

Добавляет OLAP-данные в аналитическую таблицу.

20.3. Удаление Parquet-файлов

В Postgres Pro AXE удаление Parquet-файлов выполняется в два этапа. Сначала снимки, на которые ссылаются записи из таблицы метаданных pga_data_file, необходимо отметить как устаревшие, чтобы исключить связанные Parquet-файлы из аналитических запросов. После этого Parquet-файлы можно удалять.

Перед выполнением этой инструкции убедитесь, что вам назначена роль metastore_admin. За подробной информацией обратитесь к Главе 14.

Чтобы удалить Parquet-файлы:

Отметьте снимки как устаревшие одним из следующих способов:

Чтобы отметить снимки, созданные до определённой даты и времени, как устаревшие, выполните следующий запрос:

  SELECT metastore.expire_snapshot(пороговая_дата_и_время);

Где пороговая_дата_и_время — это пороговое значение для даты и времени создания снимка.

Дата и время создания снимка содержатся в столбце snapshot_time таблицы метаданных pga_snapshot.

Поддерживаемые форматы даты и времени:

Дата в формате гггг-мм-дд.

Пример 20.3.

2024-11-28

Дата в формате гггг-мм-дд и время в формате чч:мм:сс.

Пример 20.4.

2025-11-28 12:22:46

Дата в формате гггг-мм-дд и время в формате чч:мм:сс с указанием часового пояса.

Пример 20.5.

2025-11-28 12:22:46+03

Дата в формате гггг-мм-дд и время в формате чч:мм:сс.сссссс с указанием часового пояса, где дробная часть секунд имеет не более 6 знаков.

Пример 20.6.

2025-11-28 12:22:46.123456+03

Пример 20.7.

  SELECT metastore.expire_snapshot('2025-11-13 12:22:46.123456+03');

Чтобы отметить снимки с определёнными идентификаторами как устаревшие, выполните следующий запрос:

  SELECT metastore.expire_snapshot('[список_идентификаторов_снимков]');

Где список_идентификаторов_снимков — это разделённый запятыми список идентификаторов снимков.

Идентификаторы снимков содержатся в столбце snapshot_id таблицы метаданных pga_snapshot.

Пример 20.8.

  SELECT metastore.expire_snapshot('[1,2,3,4]');

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Отмечает снимки как устаревшие, если их записи из таблицы метаданных pga_snapshot имеют значения snapshot_time, которые меньше, чем пороговая_дата_и_время, или имеют значения snapshot_id, которые равняются одному из указанных идентификаторов снимков.

Находит записи в таблице метаданных pga_data_file со значениями begin_snapshot, которые ссылаются на устаревшие снимки, и создаёт связанные записи в таблице метаданных pga_files_scheduled_for_deletion, если эти записи не были созданы при предыдущих вызовах expire_snapshot.

Примечание

Это не влияет на другие таблицы метаданных, у которых значения begin_snapshot ссылаются на устаревшие снимки.

Повторно создайте представления Postgres Pro из аналитических таблиц, связанных с Parquet-файлами, чтобы исключить эти файлы из аналитических запросов.

Важно

Повторное создание представлений Postgres Pro возможно только в периоды отсутствия пользовательской активности. Это сделано для предотвращения потери данных.

Удалите Parquet-файлы:

SELECT metastore.delete_expired_files();

После выполнения этого запроса pgpro_metastore выполняет следующие действия:

Удаляет Parquet-файлы из хранилища , а также удаляет связанные записи из таблиц метаданных pga_files_scheduled_for_deletion, pga_data_file и pga_file_column_statistics.

Пересчитывает статистику для записей, связанных с удалёнными Parquet-файлами, в таблицах метаданных pga_table_stats и pga_table_column_stats.

Пересчитывает значения column_order для записей, связанных с удалёнными Parquet-файлами, в таблице метаданных pga_column.

Часть IV. pgpro_axe

pgpro_axe представляет собой векторно-столбцовый аналитический механизм Postgres Pro, который в том числе поддерживает хранение OLAP-данных в виде Parquet-файлов в сетевом, локальном и S3-хранилищах. Это расширение состоит из следующих компонентов:

Проприетарные компоненты Postgres Pro AXE.

Компоненты ядра Postgres Pro.

DuckDB с её основной функциональностью.

Глава 21. Функции

По умолчанию функции pgpro_axe установлены в схему public. В настоящее время ALTER EXTENSION не может поместить pgpro_axe в другую схему.

21.1. Функции для чтения OLAP-данных из хранилища

read_parquet(path TEXT or TEXT[], необязательные_параметры) returns SETOF duckdb.row

Читает Parquet-файл из хранилища.

Возвращает строки DuckDB. Их можно расширить с помощью * или выбрать конкретные столбцы с помощью синтаксиса r['имя_столбца']. Чтобы выбрать конкретные столбцы, присвойте вызову функции простой псевдоним, например r:

    SELECT * FROM read_parquet('file.parquet');
SELECT r['id'], r['name'] FROM read_parquet('file.parquet') r WHERE r['age'] > 21;
SELECT COUNT(*) FROM read_parquet('file.parquet');

Обязательные параметры:

Имя

Тип

Описание

path

text или text[]

Путь к хранилищу с Parquet-файлами, которые необходимо прочитать. Может содержать шаблон поиска (glob) или массив файлов.

Необязательные параметры повторяют параметры функции DuckDB read_parquet. Чтобы указать дополнительные параметры, используйте параметр := 'значение'.

read_csv(path TEXT or TEXT[], необязательные_параметры) returns SETOF duckdb.row

Читает CSV-файл из хранилища.

Возвращает строки DuckDB. Их можно расширить с помощью * или выбрать конкретные столбцы с помощью синтаксиса r['имя_столбца']. Чтобы выбрать конкретные столбцы, присвойте вызову функции простой псевдоним, например r:

  SELECT * FROM read_csv('file.csv');
  SELECT r['id'], r['name'] FROM read_csv('file.csv') r WHERE r['age'] > 21;
  SELECT COUNT(*) FROM read_csv('file.csv');

Обязательные параметры:

Имя

Тип

Описание

path

text или text[]

Путь к хранилищу с CSV-файлами, которые необходимо прочитать. Может содержать шаблон поиска (glob) или массив файлов.

Необязательные параметры повторяют параметры функции DuckDB read_csv. Чтобы указать дополнительные параметры, используйте параметр := 'значение'.

Примечание

columns не поддерживается в настоящее время.

nullstr должен быть массивом (TEXT[])

read_json(path TEXT or TEXT[], необязательные_параметры) returns SETOF duckdb.row

Читает JSON-файл из хранилища.

Возвращает строки DuckDB. Их можно расширить с помощью * или выбрать конкретные столбцы с помощью синтаксиса r['имя_столбца']. Чтобы выбрать конкретные столбцы, присвойте вызову функции простой псевдоним, например r:

Пример 21.1.

    SELECT * FROM read_parquet('file.parquet');
SELECT r['id'], r['name'] FROM read_parquet('file.parquet') r WHERE r['age'] > 21;
SELECT COUNT(*) FROM read_parquet('file.parquet');

Обязательные параметры:

Имя

Тип

Описание

path

text или text[]

Путь к хранилищу с JSON-файлами, которые необходимо прочитать. Может содержать шаблон поиска (glob) или массив файлов.

Необязательные параметры повторяют параметры функции DuckDB read_json. Чтобы указать дополнительные параметры, используйте параметр := 'значение'.

Примечание

В настоящее время columns не поддерживается.

iceberg_scan(path TEXT, необязательные_параметры) returns SETOF duckdb.row

Читает таблицу iceberg из хранилища.

Возвращает строки DuckDB. Их можно расширить с помощью * или выбрать конкретные столбцы с помощью синтаксиса r['имя_столбца']. Чтобы выбрать конкретные столбцы, присвойте вызову функции простой псевдоним, например r:

Пример 21.2.

  SELECT * FROM iceberg_scan('data/iceberg/table');
  SELECT r['id'], r['name'] FROM iceberg_scan('data/iceberg/table') r WHERE r['age'] > 21;
  SELECT COUNT(*) FROM iceberg_scan('data/iceberg/table');

Чтобы использовать iceberg_scan, включите расширение iceberg:

  SELECT duckdb.install_extension('iceberg');

Обязательные параметры:

Имя

Тип

Описание

path

text

Путь к хранилищу с таблицей iceberg, которую необходимо прочитать.

Необязательные параметры повторяют параметры функции DuckDB iceberg_scan. Чтобы указать дополнительные параметры, используйте параметр := 'значение'.

Необязательные параметры:

Имя

Тип

Значение по умолчанию

Описание

allowed_moved_paths

boolean

false

Позволяет убедиться, что было выполнено разрешение пути. Это позволяет сканировать таблицы iceberg, расположение которых было изменено.

mode

text

''

—

metadata_compression_codec

text

'none'

—

skip_schema_inference

boolean

false

—

version

text

'version-hint.text'

—

version_name_format

text

'v%s%s.metadata.json,%s%s.metadata.json'

—

iceberg_metadata(path TEXT, необязательные_параметры) returns SETOF iceberg_metadata_record

Возвращает метаданные таблицы iceberg. Метаданные возвращаются в виде набора icerberg_metadata_record, который определяется следующим образом:

  CREATE TYPE duckdb.iceberg_metadata_record AS (
    manifest_path TEXT,
    manifest_sequence_number NUMERIC,
    manifest_content TEXT,
    status TEXT,
    content TEXT,
    file_path TEXT
  );

Чтобы использовать iceberg_metadata, включите расширение iceberg:

  SELECT duckdb.install_extension('iceberg');

Обязательные параметры:

Имя

Тип

Описание

path

text

Путь к хранилищу с таблицей iceberg, которую необходимо прочитать.

Необязательные параметры повторяют параметры функции DuckDB iceberg_metadata. Чтобы указать дополнительные параметры, используйте параметр := 'значение'.

Необязательные параметры:

Имя

Тип

Значение по умолчанию

Описание

allowed_moved_paths

boolean

false

Позволяет убедиться, что было выполнено разрешение пути. Это позволяет сканировать таблицы iceberg, расположение которых было изменено.

metadata_compression_codec

text

'none'

—

skip_schema_inference

boolean

false

—

version

text

'version-hint.text'

—

version_name_format

text

'v%s%s.metadata.json,%s%s.metadata.json'

iceberg_snapshots (path TEXT, необязательные_параметры) returns SETOF iceberg_snapshot_record

Считывает информацию о снимке из таблицы iceberg.

Возвращает метаданные снимка таблицы iceberg. Это может быть полезно для запросов с функцией «путешествия во времени» (time travel) и для изучения истории таблицы.

Пример 21.3.

  SELECT * FROM iceberg_snapshots('data/iceberg/table');

Чтобы использовать iceberg_snapshots, включите расширение iceberg:

  SELECT duckdb.install_extension('iceberg');

Обязательные параметры:

Имя

Тип

Описание

path

text

Путь к хранилищу с таблицей iceberg, которую необходимо прочитать.

Необязательные параметры повторяют параметры функции DuckDB iceberg_snapshots. Чтобы указать дополнительные параметры, используйте параметр := 'значение'.

Необязательные параметры:

Имя

Тип

Значение по умолчанию

metadata_compression_codec

text

'none'

skip_schema_inference

boolean

false

version

text

'version-hint.text'

version_name_format

text

'v%s%s.metadata.json,%s%s.metadata.json'

delta_scan(path TEXT) returns SETOF duckdb.row

Считывает набор данных Delta из хранилища.

Возвращает строки DuckDB. Их можно расширить с помощью * или выбрать конкретные столбцы с помощью синтаксиса r['имя_столбца']. Чтобы выбрать конкретные столбцы, присвойте вызову функции простой псевдоним, например r:

Пример 21.4.

  SELECT * FROM delta_scan('/path/to/delta/dataset');
  SELECT r['id'], r['name'] FROM delta_scan('/path/to/delta/dataset') r WHERE r['age'] > 21;
  SELECT COUNT(*) FROM delta_scan('/path/to/delta/dataset');

Чтобы использовать delta_scan, включите расширение delta:

  SELECT duckdb.install_extension('delta');

Обязательные параметры:

Имя

Тип

Описание

path

text

Путь к хранилищу с набором данных Delta, который необходимо прочитать.

21.2. Функции JSON

pgpro_axe поддерживает все функции и агрегаты JSON DuckDB. Функции JSON/JSONB Postgres Pro не поддерживаются.

21.3. Функции для работы с типами-объединениями

union_extract(union_col, tag) returns duckdb.unresolved_type

Извлекает значение из типа-объединения за счёт указания имени тега члена, к которому необходимо получить доступ.

Пример 21.5.

-- Извлечь строковое значение, если объединение содержит строку
SELECT union_extract(my_union_column, 'string') FROM my_table;

-- Извлечь целочисленное значение из объединения
SELECT union_extract(data_field, 'integer') AS extracted_int FROM mixed_data;

Обязательные параметры:

Имя

Тип

Описание

union_col

duckdb.union or duckdb.unresolved_type

Столбец-объединение, из которого необходимо получить значение

tag

text

Имя тега извлекаемого члена объединения

union_tag(union_col) returns duckdb.unresolved_type

Возвращает имя тега текущего активного члена в типе‑объединении.

Пример 21.6.

-- Получить активный тег для каждой строки
SELECT union_tag(my_union_column) AS active_type FROM my_table;

-- Отфильтровать строки, используя тег объединения
SELECT * FROM my_table WHERE union_tag(data_field) = 'string';

Обязательные параметры:

Имя

Тип

Описание

union_col

duckdb.union or duckdb.unresolved_type

Столбец-объединение, из которого требуется получить тег

21.4. Функции для работы с ассоциативными массивами

cardinality(map_col duckdb.map) returns numeric

Возвращает размер ассоциативного массива (количество пар ключ-значение).

Пример 21.7.

-- Получить количество записей в ассоциативном массиве
SELECT cardinality(r['map_col']) as size
FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
-- Возвращает: 3

-- Пустой ассоциативный массив
SELECT cardinality(r['map_col']) as size
FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
-- Возвращает: 0

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, размер которого необходимо получить

element_at(map_col duckdb.map, key duckdb.unresolved_type) returns duckdb.unresolved_type

Возвращает значение по указанному ключу в виде массива.

Пример 21.8.

-- Получить значение для определённого ключа
SELECT element_at(r['map_col'], 'a') as value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: {1}

-- Несуществующий ключ
SELECT element_at(r['map_col'], 'c') as value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: {}

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь значение

key

duckdb.unresolved_type

Ключ, для которого необходимо извлечь значение

map_concat(map_col duckdb.map, map_col2 duckdb.map) returns duckdb.map

Объединяет два ассоциативных массива. При конфликте ключей значение берётся из последнего ассоциативного массива.

Пример 21.9.

-- Объединить два ассоциативных массива
SELECT map_concat(r1['map1'], r2['map2']) as merged
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map1 $$) r1,
     duckdb.query($$ SELECT MAP(['b', 'c'], [3, 4]) as map2 $$) r2;
-- Возвращает: {a=1, b=3, c=4}

-- Примечание: значение 'b' из map2 (3) заменяет значение (2) из map1

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Первый ассоциативный массив для объединения

map_col2

duckdb.map

Второй ассоциативный массив для объединения

map_contains(map_col duckdb.map, key duckdb.unresolved_type) returns boolean

Проверяет, содержит ли ассоциативный массив указанный ключ.

Пример 21.10.

-- Проверить, существует ли ключ
SELECT map_contains(r['map_col'], 'a') as has_key
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: t (true)

-- Проверить отсутствующий ключ
SELECT map_contains(r['map_col'], 'c') as has_key
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: f (false)

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, в котором необходимо проверить ключ

key

duckdb.unresolved_type

Ключ, который необходимо проверить

map_contains_entry(map_col duckdb.map, key duckdb.unresolved_type, value duckdb.unresolved_type) boolean

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

Пример 21.11.

-- Проверить наличие пары ключ-значение
SELECT map_contains_entry(r['map_col'], 'a', 1) as has_entry
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: t (true)

-- Проверить наличие записи с неправильным значением у существующего ключа
SELECT map_contains_entry(r['map_col'], 'a', 2) as has_entry
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: f (false)

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, в котором необходимо проверить пару ключ-значение

key

duckdb.unresolved_type

Ключ, который необходимо проверить

value

duckdb.unresolved_type

Значение, которое необходимо проверить

map_contains_value(map_col duckdb.map, value duckdb.unresolved_type) returns boolean

Проверяет, содержит ли ассоциативный массив указанное значение.

Пример 21.12.

-- Проверить наличие значения
SELECT map_contains_value(r['map_col'], 1) as has_value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: t (true)

-- Проверить несуществующее значение
SELECT map_contains_value(r['map_col'], 3) as has_value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: f (false)

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, в котором необходимо проверить значение

value

duckdb.unresolved_type

Значение, которое необходимо проверить

map_entries(map_col duckdb.map) returns duckdb.struct[]

Возвращает массив struct (key, value) для каждой пары ключ-значение в ассоциативном массиве.

Пример 21.13.

-- Получить все пары ключ-значение в виде struct
SELECT map_entries(r['map_col']) as entries
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: {"(a,1)","(b,2)"}

-- Получить доступ к отдельным полям типа struct
SELECT unnest(map_entries(r['map_col'])) as entry
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь массивы struct

map_extract(map_col duckdb.map, key duckdb.unresolved_type) returns duckdb.unresolved_type

Извлекает значение из ассоциативного массива по указанному ключу. Если ключ не существует, возвращает пустой массив.

Пример 21.14.

-- Извлечь значение из ассоциативного массива
SELECT map_extract(r['map_col'], 'a') as value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: {1}

-- Извлечь несуществующий ключ
SELECT map_extract(r['map_col'], 'c') as value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: {}

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь значение

key

duckdb.unresolved_type

Ключ, который необходимо использовать для извлечения значения

map_extract_value(map_col duckdb.map, key duckdb.unresolved_type) returns duckdb.unresolved_type

Возвращает значение для указанного ключа или NULL, если ключ отсутствует в ассоциативном массиве.

Пример 21.15.

-- Извлечь одно значение (не в виде массива)
SELECT map_extract_value(r['map_col'], 'a') as value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: 1

-- Несуществующий ключ возвращает значение NULL
SELECT map_extract_value(r['map_col'], 'c') as value
FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
-- Возвращает: NULL

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь значение

key

duckdb.unresolved_type

Ключ, для которого необходимо извлечь значение

map_from_entries(entries duckdb.struct[]) returns duckdb.map

Создаёт ассоциативный массив из массива struct (k, v).

Пример 21.16.

-- Создать ассоциативный массив из массива данных типа struct
SELECT map_from_entries(r['entries']) as new_map
FROM duckdb.query($$
    SELECT [{'k': 'a', 'v': 1}, {'k': 'b', 'v': 2}] as entries
$$) r;
-- Возвращает: {a=1, b=2}

-- Операция, обратная map_entries
SELECT map_from_entries(map_entries(r['map_col'])) as reconstructed
FROM duckdb.query($$ SELECT MAP(['x', 'y'], [10, 20]) as map_col $$) r;
-- Возвращает: {x=10, y=20}

Обязательные параметры:

Имя

Тип

Описание

entries

duckdb.struct[]

Массив значений struct с полями 'k' (ключ) и 'v' (значение)

map_keys(map_col duckdb.map) returns duckdb.unresolved_type

Возвращает все ключи из ассоциативного массива в виде массива.

Пример 21.17.

-- Получить все ключи из ассоциативного массива
SELECT map_keys(r['map_col']) as keys
FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
-- Возвращает: {a,b,c}

-- Пустой ассоциативный массив
SELECT map_keys(r['map_col']) as keys
FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
-- Возвращает: {}

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь ключи

map_values(map_col duckdb.map) returns duckdb.unresolved_type

Возвращает все значения из ассоциативного массива в виде массива.

Пример 21.18.

-- Получить все значения из ассоциативного массива
SELECT map_values(r['map_col']) as values
FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
-- Возвращает: {1,2,3}

-- Пустой ассоциативный массив
SELECT map_values(r['map_col']) as values
FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
-- Возвращает: {}

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь значения

21.5. Агрегатные функции

approx_count_distinct(expression) returns BIGINT

Приближённо вычисляет количество уникальных элементов с использованием алгоритма HyperLogLog. Это значительно быстрее, чем COUNT(DISTINCT ...) для больших наборов данных, при этом погрешность остаётся небольшой.

Пример 21.19.

-- Приблизительное количество уникальных идентификаторов клиентов
SELECT approx_count_distinct(customer_id) FROM orders;

-- Сравнить с точным количеством
SELECT
    approx_count_distinct(customer_id) AS approx_distinct,
    COUNT(DISTINCT customer_id) AS exact_distinct
FROM orders;

Обязательные параметры:

Имя

Тип

Описание

expression

any

Выражение, для которого необходимо считать уникальные значения

21.6. Функции обработки выборок

TABLESAMPLE (sampling_method(percentage | rows))

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

Пример 21.20.

-- Выбрать 10% строк из таблицы
SELECT * FROM large_table TABLESAMPLE SYSTEM(10);

-- Выбрать примерно 1000 строк
SELECT * FROM events TABLESAMPLE SYSTEM(1000 ROWS);

-- Выбрать из файлов озера данных
SELECT * FROM read_parquet('s3://datalake/**/*.parquet') TABLESAMPLE SYSTEM(5);

-- Использовать выборку для быстрого анализа данных
SELECT
    region,
    COUNT(*) as sample_count,
    AVG(revenue) as avg_revenue
FROM sales_data TABLESAMPLE SYSTEM(2)
GROUP BY region;

-- Выбрать из соединений для ускорения
SELECT c.name, COUNT(o.id) as order_count
FROM customers c
JOIN orders o TABLESAMPLE SYSTEM(10) ON c.id = o.customer_id
GROUP BY c.name;

Методы извлечения выборки:

SYSTEM: случайная выборка на уровне хранилища (быстрее, приблизительный процент).

BERNOULLI: построчная случайная выборка (медленнее, точный процент).

Пример 21.21.

-- Системная выборка (рекомендуется для больших таблиц)
SELECT * FROM huge_table TABLESAMPLE SYSTEM(1);

-- Выборка Bernoulli (точный процент)
SELECT * FROM medium_table TABLESAMPLE BERNOULLI(5);

Сценарии использования:

Исследование данных: быстрый анализ больших наборов данных.

Тестирование производительности: проверка запросов на выборочных данных.

Профилирование данных: изучение закономерностей распределения данных.

Разработка ETL-операций: создание конвейеров на выборочных данных.

Проверка качества: контроль качества данных на выборках.

Обязательные параметры:

Имя

Тип

Описание

sampling_method

keyword

SYSTEM или BERNOULLI

percentage

numeric

Процент строк для выборки (0-100)

Необязательные параметры:

Имя

Тип

Описание

rows

integer

Примерное количество строк для выборки (использовать с ключевым словом ROWS)

21.7. Функции для работы с типами данных времени

time_bucket(bucket_width INTERVAL, timestamp_col TIMESTAMP, origin TIMESTAMP) returns TIMESTAMP

Группирует временные метки в интервалы для анализа временных рядов. Эта функция совместима с функцией time_bucket в TimescaleDB, что упрощает миграцию и обеспечивает совместимость.

Пример 21.22.

-- Группировать события по часам
SELECT time_bucket(INTERVAL '1 hour', created_at) as hour_bucket, COUNT(*)
FROM events
GROUP BY hour_bucket
ORDER BY hour_bucket;

-- Группировать с интервалом 15 минут
SELECT time_bucket(INTERVAL '15 minutes', timestamp_col), AVG(value)
FROM sensor_data
WHERE timestamp_col >= '2024-01-01'
GROUP BY 1
ORDER BY 1;

Обязательные параметры:

Имя

Тип

Описание

bucket_width

interval

Интервал времени для группировки (например, '1 hour', '15 minutes')

timestamp_col

timestamp

Столбец с временными метками для группировки

Необязательные параметры:

Имя

Тип

Описание

origin

timestamp

Точка отсчёта для группировки. Интервалы выровнены относительно этой временной метки.

strftime (timestamp_expr, format_string) returns TEXT

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

Пример 21.23.

-- Форматировать текущую временную метку
SELECT strftime(NOW(), '%Y-%m-%d %H:%M:%S') AS formatted_time;

-- Форматировать временные метки в разных форматах
SELECT
    order_id,
    strftime(created_at, '%Y-%m-%d') AS order_date,
    strftime(created_at, '%H:%M') AS order_time,
    strftime(created_at, '%A, %B %d, %Y') AS readable_date
FROM orders;

-- Использовать для секционирования экспорта в файлы
COPY (SELECT * FROM events WHERE event_date = '2024-01-01')
TO 's3://bucket/events/' || strftime('2024-01-01'::timestamp, '%Y/%m/%d') || '/events.parquet';

Основные коды форматирования:

%Y: четырёхзначный год (2024).

%m: месяц в числовом формате (01-12).

%d: день месяца (01-31).

%H: час (00-23).

%M: минута (00-59).

%S: секунда (00-59).

%A: полное название дня недели (понедельник).

%B: полное название месяца (январь).

Необязательные параметры:

Имя

Тип

Описание

timestamp_expr

timestamp

Значение временной метки для форматирования

format_string

text

Строка с кодами форматирования

strptime(string_expr, format_string) returns TIMESTAMP

Преобразует строки во временные метки с помощью кодов форматирования.

Пример 21.24.

-- Разобрать строки с датами
SELECT strptime('2024-01-15 14:30:00', '%Y-%m-%d %H:%M:%S') AS parsed_timestamp;

-- Разобрать разные форматы
SELECT
    strptime('Jan 15, 2024', '%b %d, %Y') AS date1,
    strptime('15/01/2024', '%d/%m/%Y') AS date2,
    strptime('2024-01-15T14:30:00Z', '%Y-%m-%dT%H:%M:%SZ') AS iso_date;

-- Разобрать временные метки в журнале
SELECT
    log_id,
    strptime(timestamp_string, '%Y-%m-%d %H:%M:%S') AS parsed_time,
    message
FROM raw_logs;

Обязательные параметры:

Имя

Тип

Описание

string_expr

text

Строка для преобразования во временную метку

format_string

text

Строка форматирования с описанием формата входной строки

epoch(timestamp_expr) returns BIGINT

Преобразует временные метки в секунды эпохи Unix (количество секунд, прошедших с 1970-01-01 00:00:00 UTC).

Пример 21.25.

-- Получить текущее время эпохи Unix
SELECT epoch(NOW()) AS current_epoch;

-- Преобразовать временные метки для использования в API
SELECT
    event_id,
    epoch(event_timestamp) AS epoch_seconds
FROM events;

-- Отфильтровать, используя время эпохи Unix
SELECT * FROM events
WHERE epoch(created_at) > 1640995200; -- After 2022-01-01

Обязательные параметры:

Имя

Тип

Описание

map_col

duckdb.map

Ассоциативный массив, из которого необходимо извлечь параметры

key

duckdb.unresolved_type

Ключ, который необходимо найти в ассоциативном массиве

epoch_ms(timestamp_expr) returns BIGINT

Преобразует временные метки в миллисекунды эпохи Unix.

Пример 21.26.

-- Высокоточная временная метка для JavaScript
SELECT epoch_ms(NOW()) AS timestamp_ms;

-- Для данных временных рядов
SELECT
    sensor_id,
    epoch_ms(reading_time) AS timestamp_ms,
    value
FROM sensor_readings;

Обязательные параметры:

Имя

Тип

Описание

timestamp_expr

timestamp

Временная метка, которую необходимо преобразовать в миллисекунды эпохи Unix

epoch_ms(milliseconds) returns TIMESTAMP

Преобразует миллисекунды эпохи Unix во временную метку.

Пример 21.27.

-- Преобразовать миллисекунды эпохи Unix во временную метку
SELECT epoch_ms(1640995200000) AS timestamp_from_ms; -- 2022-01-01 00:00:00

-- Преобразовать сохранённые миллисекунды обратно во временные метки
SELECT
    event_id,
    epoch_ms(timestamp_ms) AS event_time
FROM events;

Обязательные параметры:

Имя

Тип

Описание

milliseconds

bigint

Миллисекунды, прошедшие с начала эпохи Unix

epoch_us(timestamp_expr) returns BIGINT

Преобразовывает временные метки в микросекунды эпохи Unix.

Пример 21.28.

-- Временные метки с точностью до микросекунды
SELECT epoch_us(NOW()) AS timestamp_us;

Обязательные параметры:

Имя

Тип

Описание

timestamp_expr

timestamp

Временная метка, которую необходимо преобразовать в микросекунды эпохи Unix

epoch_ns(timestamp_expr) returns BIGINT

Преобразовывает временные метки в наносекунды эпохи Unix.

Пример 21.29.

-- Временные метки с точностью до наносекунды
SELECT epoch_ns(NOW()) AS timestamp_ns;

Обязательные параметры:

Имя

Тип

Описание

timestamp_expr

timestamp

Временная метка, которую необходимо преобразовать в наносекунды эпохи Unix

make_timestamp(microseconds) returns TIMESTAMP

Создаёт временную метку из микросекунд, прошедших с начала эпохи Unix (1970-01-01 00:00:00 UTC).

Пример 21.30.

-- Создать временную метку из текущего времени в микросекундах эпохи Unix
SELECT make_timestamp(epoch_us(NOW())) AS reconstructed_timestamp;

-- Создать определённую временную метку
SELECT make_timestamp(1640995200000000) AS new_years_2022; -- 2022-01-01 00:00:00

Обязательные параметры:

Имя

Тип

Описание

microseconds

BIGINT

Микросекунды, прошедшие с начала эпохи Unix

make_timestamptz(microseconds) returns TIMESTAMPTZ

Создаёт временную метку с часовым поясом из микросекунд, прошедших с начала эпохи Unix.

Пример 21.31.

-- Создать timestamptz из текущих микросекунд эпохи
SELECT make_timestamptz(epoch_us(NOW())) AS reconstructed_timestamptz;

-- Создать конкретную timestamptz
SELECT make_timestamptz(1640995200000000) AS new_years_2022_tz;

Обязательные параметры:

Имя

Тип

Описание

microseconds

bigint

Микросекунды, прошедшие с начала эпохи Unix

21.8. Функции администрирования DuckDB

duckdb.install_extension(extension_name TEXT, repository TEXT DEFAULT 'core') returns bool

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

Пример 21.32.

  SELECT duckdb.install_extension('iceberg');
SELECT duckdb.install_extension('avro', 'community');

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

Пример 21.33.

  GRANT ALL ON FUNCTION duckdb.install_extension(TEXT, TEXT) TO my_admin;

Обязательные параметры:

Имя

Тип

Описание

extension_name

text

Имя расширения, которое необходимо установить

duckdb.load_extension(extension_name TEXT) returns void

Загружает расширение DuckDB только для текущего сеанса. В отличие от функции duckdb.install_extension, это не настраивает автоматическую загрузку расширения в будущих сеансах.

Пример 21.34.

SELECT duckdb.load_extension('iceberg');

Обязательные параметры:

Имя

Тип

Описание

extension_name

text

Имя загружаемого расширения

duckdb.autoload_extension(extension_name TEXT, autoload BOOLEAN) returns void

Указывает, выполняется ли автоматическая загрузка установленного расширения в новых сеансах.

Пример 21.35.

-- Отключить автозагрузку расширения
SELECT duckdb.autoload_extension('iceberg', false);

-- Включить автозагрузку расширения
SELECT duckdb.autoload_extension('iceberg', true);

Обязательные параметры:

Имя

Тип

Описание

extension_name
text

Имя расширения

autoload

boolean

Указывает, выполняется ли автоматическая загрузка расширения в новых сеансах

duckdb.query(query TEXT) returns SETOF duckdb.row

Напрямую выполняет запрос SELECT к DuckDB. Это может потребоваться, если запрос проще составить в синтаксисе DuckDB или если требуется использовать функцию, которая пока не поддерживается pgpro_axe.

Пример 21.36.

В этом запросе FROM помещён перед SELECT и используется списковое включение (list comprehension). Обе эти функциональности не поддерживаются Postgres Pro.

  SELECT * FROM duckdb.query('FROM range(10) as a(a) SELECT [a for i in generate_series(0, a)] as arr');

duckdb.raw_query(extension_name TEXT) returns void

Напрямую выполняет запрос к DuckDB.

В отличие от duckdb.query эта функция позволяет выполнять любой запрос, а не только SELECT. Основной недостаток заключается в том, что результат запроса записывается в журнал вместо того, чтобы вернуть строки. Рекомендуется по возможности использовать duckdb.query.

duckdb.recycle_ddb() returns void

pgpro_axe оставляет экземпляр DuckDB открытым между транзакциями. Это делается для того, чтобы сохранить состояние уровня сеанса, например вручную выполненные команды SET. Чтобы очистить состояние уровня сеанса, открытый экземпляр DuckDB можно закрыть, вызвав эту функцию.

Пример 21.37.

  CALL duckdb.recycle_ddb();

21.9. Функции управления секретами

За подробной информацией о секретах обратитесь к Разделу 14.5.

duckdb.create_simple_secret(type TEXT, key_id TEXT, secret TEXT, region TEXT, необязательные_параметры) returns void

Создаёт простой секрет для доступа к сервисам облачного хранения данных, таким как S3, GCS или R2.

Пример 21.38.

-- Создать секрет S3
SELECT duckdb.create_simple_secret(
    type := 'S3',
    key_id := 'your_access_key',
    secret := 'your_secret_key',
    region := 'us-east-1'
);

-- Создать секрет S3 с токеном сеанса
SELECT duckdb.create_simple_secret(
    type := 'S3',
    key_id := 'your_access_key',
    secret := 'your_secret_key',
    region := 'us-east-1',
    session_token := 'your_session_token'
);

Обязательные параметры:

Имя

Тип

Описание

type

text

Тип секрета (например, 'S3', 'GCS', 'R2')

key_id

text

Идентификатор ключа доступа или его эквивалент

secret

text

Секретный ключ или его эквивалент

Необязательные параметры:

Имя

Тип

Описание

session_token

text

Токен сеанса для временных учётных данных

endpoint

text

Пользовательский URL-адрес конечной точки

url_style

text

Стиль URL.

Возможные значения:

'vhost'

'path'

use_ssl
text

Указывает, используется ли SSL.

Возможные значения:

true

false

Значение по умолчанию: true.

scope

text

Область действия секрета.

Значение по умолчанию: ''

region

text

Регион для выбора службы

Глава 22. Поддерживаемые расширения

pgpro_axe поддерживает следующие расширения.

iceberg

Поддержка iceberg позволяет читать таблицы и метаданные iceberg.

За подробной информацией о функциях iceberg обратитесь к Главе 21.

delta

Поддержка delta позволяет читать файлы delta Lake с помощью delta_scan.

Глава 23. Параметры конфигурации

Параметры pgpro_axe можно настроить в файле postgresql.conf.

duckdb.force_execution

Принудительно выполняет запросы в DuckDB. Это необходимо, если запрос относится к таблицам Postgres Pro. Если используется функциональность, доступная только в DuckDB, запросы автоматически выполняются в DuckDB.

К функциональности, доступной только в DuckDB, относится чтение таблиц DuckDB, использование функций DuckDB (например, read_parquet), а также выполнение COPY в удалённое хранилище (например, s3://).

Значение по умолчанию: false.

Доступ: все пользователи.

duckdb.default_collation

Устанавливает правила сортировки по умолчанию для строковых операций и сортировки DuckDB. Этот параметр позволяет настраивать поведение сравнения строк с учётом конкретной локали.

Пример 23.1.

"en_us"

"de_de"

"C"

Значение по умолчанию: "" — используется значение по умолчанию из DuckDB.

Доступ: только суперпользователь.

23.1. Безопасность

duckdb.postgres_role

Указывает роль Postgres Pro, которой позволено выполнять запросы в DuckDB и управлять секретами. Если указанная роль не существует в момент выполнения CREATE EXTENSION pgpro_axe, она будет создана автоматически.

Значение по умолчанию: "" — только суперпользователи.

Доступ: требуется перезапуск.

duckdb.disabled_filesystems

Отключает хранилища для pgpro_axe.

Возможные значения:

LocalFileSystem: отключить локальные хранилища.

S3FileSystem: отключить S3-хранилища.

HTTPFileSystem: отключить сетевые хранилища.

По умолчанию отключено локальное хранилище:

Пример 23.2.

  postgres=> show duckdb.disabled_filesystems;
  duckdb.disabled_filesystems
  -----------------------------
  LocalFileSystem
  (1 row)

Чтобы указать несколько хранилищ, разделите их имена запятыми без пробелов:

Пример 23.3.

  postgres=# ALTER SYSTEM SET duckdb.disabled_filesystems TO 'LocalFileSystem,HTTPFileSystem';
  ALTER SYSTEM
  -- after server reboot --
  postgres=# show duckdb.disabled_filesystems;
    duckdb.disabled_filesystems
  --------------------------------
  LocalFileSystem,HTTPFileSystem
  (1 row)

Доступ: только суперпользователь.

duckdb.autoinstall_known_extensions

Указывает, разрешена ли автоматическая установка известных расширений, если они необходимы для выполнения запроса DuckDB.

Возможные значения:

true

false

По умолчанию только суперпользователи могут выполнять функции duckdb.install_extension и duckdb.autoload_extension. Это сделано для того, чтобы другие пользователи не устанавливали расширения, которые могут повлиять на безопасность или помешать работе баз данных.

Пользователям доступны только расширения, отмеченные в DuckDB как автоматически устанавливаемые. Чтобы ограничить использование таких расширений, укажите false для duckdb.autoinstall_known_extensions.

Значение по умолчанию: true.

Доступ: только суперпользователь.

duckdb.autoload_known_extensions

Указывает, разрешена ли автоматическая загрузка известных расширений, если они необходимы для выполнения запроса DuckDB.

Значение по умолчанию: true.

Доступ: только суперпользователь

duckdb.allow_community_extensions

Указывает, разрешена ли установка расширений сообщества.

Значение по умолчанию: false.

Доступ: только суперпользователь.

duckdb.unsafe_allow_execution_inside_functions

Разрешает выполнение функций Postgres Pro в DuckDB.

Важно

Эта функциональность отключена по умолчанию, так как её использование может привести к неработоспособности Postgres Pro AXE.

Значение по умолчанию: false.

Доступ: только суперпользователь.

duckdb.enable_external_access

Предоставляет DuckDB доступ к внешним ресурсам (например, HTTP, S3).

Важно

Эта функциональность отключена по умолчанию, так как её использование может привести к неработоспособности Postgres Pro AXE.

Значение по умолчанию: true.

Доступ: только суперпользователь.

23.2. Управление ресурсами

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

duckdb.max_memory / duckdb.memory_limit

Максимальный объём памяти в мегабайтах, который Postgres Pro может использовать для обработки одного запроса. Этот параметр можно настраивать для каждого сеанса. При нулевом значении используется 80% оперативной памяти.

Пример 23.4.

4096 (4 ГБ)

8192 (8 ГБ)

1024 (1 ГБ)

Значение по умолчанию: 4096 (4 ГБ).

Доступ: только суперпользователь.

duckdb.threads / duckdb.worker_threads

Максимальное количество потоков (ядер процессора), которое может использовать Postgres Pro для обработки одного запроса. Этот параметр можно настраивать для каждого сеанса.

Значение по умолчанию: -1 — значение равно количеству ядер процессора.

Доступ: только суперпользователь.

duckdb.max_workers_per_postgres_scan

Максимальное количество рабочих процессов Postgres Pro, используемых для одного сканирования Postgres Pro. Этот параметр аналогичен max_parallel_workers_per_gather.

Значение по умолчанию: 2.

Доступ: все пользователи.

duckdb.threads_for_postgres_scan

Максимальное количество потоков DuckDB для каждого сканирования Postgres Pro. Этот параметр управляет уровнем распараллеливания в DuckDB при сканировании таблиц Postgres Pro.

Значение по умолчанию: 2.

Доступ: все пользователи.

23.3. Продвинутое использование

duckdb.convert_unsupported_numeric_to_double

Преобразует типы NUMERIC с неподдерживаемыми значениями precision/scale в DOUBLE вместо выдачи ошибки.

Возможные значения:

true

false

DuckDB поддерживает типы NUMERIC/DECIMAL с точностью от 1 до 38 и масштабом от 0 до 38 (при условии, что масштаб ≤ точности). Для NUMERIC, выходящих за эти пределы:

Если указано true, неподдерживаемые значения типа NUMERIC преобразуются в DOUBLE с возможной потерей точности.

Если указано false, неподдерживаемые значения типа NUMERIC вызывают ошибку.

Значение по умолчанию: false.

Доступ: все пользователи.

duckdb.unsafe_allow_mixed_transactions

Позволяет выполнять смешанные транзакции между DuckDB и Postgres Pro.

Важно

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

Значение по умолчанию: false.

Доступ: все пользователи.

23.4. Хранилище

duckdb.temporary_directory

Указывает каталог, в котором DuckDB записывает временные файлы. По умолчанию DuckDB использует каталог внутри каталога Postgres Pro (DataDir/pg_duckdb/temp). Этот параметр может быть полезен для указания более быстрого хранилища (например, SSD) или более эффективного управления дисковым пространством.

Значение по умолчанию: "DataDir/pg_duckdb/temp".

Доступ: только суперпользователь.

duckdb.max_temp_directory_size / duckdb.max_temporary_directory_size

Максимальный объём данных, который может быть сохранён во временном каталоге DuckDB. Этот параметр позволяет предотвратить использование всего доступного дискового пространства из‑за неправильно работающих запросов.

Пример 23.5.

"10GB"

"500MB"

"2TB"

Значение по умолчанию: "" — без ограничения.

Доступ: только суперпользователь.

duckdb.extension_directory

Указывает каталог, в котором DuckDB хранит свои расширения. По умолчанию расширения хранятся в каталоге Postgres Pro (DataDir/pg_duckdb/extensions). Это полезно для управления хранилищем расширений или для совместного использования расширений несколькими экземплярами Postgres Pro.

Значение по умолчанию: "DataDir/pg_duckdb/extensions".

Доступ: только суперпользователь.

23.5. Параметры для разработчика

duckdb.allow_unsigned_extensions

Позволяет DuckDB загружать расширения с неверными или отсутствующими подписями. Этот параметр особенно полезен при разработке расширений DuckDB.

Значение по умолчанию: false.

Доступ: только суперпользователь.

duckdb.log_pg_explain

Регистрирует в журнале план команды EXPLAIN для сканирования в Postgres Pro на уровне журнала NOTICE. Этот параметр полезен для отладки выполнения запросов и понимания того, как DuckDB взаимодействует с таблицами Postgres Pro.

Значение по умолчанию: false.

Доступ: все пользователи.

Глава 24. Транзакции

pgpro_axe поддерживает транзакции с несколькими операторами. При этом существует одно ограничение, которое необходимо для обеспечения гарантий ACID: в одной транзакции невозможно записывать одновременно в таблицу Postgres Pro и DuckDB.

Аналогичным образом в транзакции можно использовать DDL-команды (например, CREATE/DROP TABLE) с таблицами DuckDB, но при этом подобные операторы нельзя объединять с DDL-командами, которые содержат объекты Postgres Pro.

Кроме того, это ограничение можно полностью отключить и разрешить в одной и той же транзакции запись как в DuckDB, так и в Postgres Pro. Для этого укажите true для duckdb.unsafe_allow_mixed_transactions. Однако этого делать не рекомендуется, так как в результате транзакция может быть зафиксирована в DuckDB, но не в Postgres Pro. Это может привести к несогласованности данных и их потере.

Пример 24.1.

В результате следующего запроса duckdb_table может быть удалена, и при этом её содержимое не будет скопировано в pg_table:

  BEGIN;
SET LOCAL duckdb.unsafe_allow_mixed_transactions TO true;
CREATE TABLE pg_table AS SELECT * FROM duckdb_table;
DROP TABLE duckdb_table;
COMMIT;

Глава 25. Синтаксис

25.1. Создание таблицы Postgres Pro

Пример 25.1.

-- Стандартная таблица Postgres Pro
CREATE TABLE orders (
  order_id SERIAL PRIMARY KEY,
  product_name TEXT,
  amount NUMERIC,
  order_date DATE
);

INSERT INTO orders (product_name, amount, order_date)
VALUES ('Laptop', 1200.00, '2024-07-01'),
      ('Keyboard', 75.50, '2024-07-01'),
      ('Mouse', 25.00, '2024-07-02');

25.2. Запросы к таблицам Postgres Pro

Для аналитических запросов к существующим таблицам Postgres Pro используйте стандартный SQL. Специальный синтаксис не требуется. pgpro_axe автоматически ускоряет выполнение этих запросов, если вы указываете true для duckdb.force_execution.

Пример 25.2.

SET duckdb.force_execution = true;
-- Стандартный SELECT к таблице Postgres Pro
SELECT
  category,
  AVG(price) as avg_price,
  COUNT(*) as item_count
FROM
  products -- Обычная таблица Postgres Pro
GROUP BY
  category
ORDER BY
  avg_price DESC;

SET duckdb.force_execution = true;
-- Стандартный SELECT к таблице Postgres Pro
SELECT
  category,
  AVG(price) as avg_price,
  COUNT(*) as item_count
FROM
  products -- Обычная таблица Postgres Pro
GROUP BY
  category
ORDER BY
  avg_price DESC;

25.3. Запросы к внешним файлам

Чтобы выполнять запросы к файлам из озера данных (например, из локального или S3-хранилища), используйте функции read_*.

Чтобы получить доступ к столбцам, используйте синтаксис r['имя_столбца'].

Пример 25.3.

-- Выполнить запрос к одному Parquet-файлу
SELECT
  r['product_id'],
  r['review_text']
FROM
  read_parquet('s3://my-bucket/reviews.parquet') r -- 'r' is a required alias
LIMIT 100;

-- Выполнить запрос к нескольким CSV-файлам, используя glob-шаблон
SELECT
  r['timestamp'],
  r['event_type'],
  COUNT(*) as event_count
FROM
  read_csv('s3://my-datalake/logs/2024-*.csv') r
GROUP BY
  r['timestamp'],
  r['event_type'];

25.4. Соединение данных Postgres Pro с внешними данными

В одном запросе можно соединять таблицы Postgres Pro с внешними источниками данных.

Пример 25.4.

-- Соединить локальную таблицу Postgres Pro 'customers' с удалённым Parquet-файлом 'orders'
SELECT
  c.customer_name,
  c.signup_date,
  SUM(r['order_total']) AS total_spent
FROM
  customers c -- This is a Postgres Pro table
JOIN
  read_parquet('s3://my-bucket/orders/*.parquet') r ON c.customer_id = r['customer_id']
WHERE
  c.status = 'active'
GROUP BY
  c.customer_name,
  c.signup_date
ORDER BY
  total_spent DESC;

25.5. Использование duckdb.query()

duckdb.query() — это расширенная функция, которая не требуется для большинства запросов. Используйте её только в тех случаях, когда необходимо выполнить запрос, использующий специфичный для DuckDB синтаксис, который недопустим в Postgres Pro.

Пример 25.5.

-- Этот запрос использует синтаксис оператора PIVOT из DuckDB, поэтому его необходимо обернуть в duckdb.query()
SELECT * FROM duckdb.query($$
  PIVOT sales_summary
  ON product_category
  USING SUM(total_sales)
  GROUP BY region;
$$);

Часть V. pgpro_metastore

pgpro_metastore — расширение Postgres Pro для обработки и хранения OLAP-данных.

Это расширение позволяет управлять аналитическими таблицами с OLAP-данными. Строки аналитических таблиц размещаются в виде Parquet-файлов в локальном, сетевом или S3-хранилище. Метаданные аналитических таблиц хранятся в таблицах метаданных (для соответствия требованиям спецификации DuckLake).

Расширение pgpro_metastore предоставляет следующие преимущества:

Ускорение выполнения аналитических запросов в 10–30 раз и уменьшение объёма сохраняемых OLAP-данных в 5–10 раз по сравнению с таблицами-кучами.

Использование инструментов, совместимых с Parquet, таких как Jupyter и Notebook, для обработки OLAP-данных.

Неограниченные возможности масштабирования хранилищ для увеличения объёма сохраняемых OLAP-данных и ускорения доступа к ним.

Независимый механизм масштабирования вычислительных ресурсов хранилищ.

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

Гибкую настройку ограничений доступа к хранилищам, аналогичную Postgres Pro.

Изменения данных в рамках транзакций и согласованность между OLAP-данными и метаданными.

Транзакции с постоянными идентификаторами, которые позволяют запрашивать OLAP-данные и метаданные на основе определённых идентификаторов и восстанавливать данные на определённый момент времени.

Сбор статистики для всех столбцов и транзакций, что позволяет запрашивать необходимые Parquet-файлы.

Глава 26. Настройка каталога pgpro_metastore

Таблицы метаданных pgpro_metastore хранятся в схеме axe_catalog, или в так называемом каталоге pgpro_metastore. Этот каталог можно создать на сервере pgpro_metastore или на отдельном сервере.

Чтобы настроить каталог pgpro_metastore:

Настройте подключение к серверу с каталогом pgpro_metastore:

SELECT metastore.define_catalog_connection('хост', 'порт', 'имя_БД', 'имя_пользователя', 'пароль')

Где:

хост и порт: имя хоста и номер порта для сервера с каталогом pgpro_metastore.

имя_БД: имя базы данных где создан каталог pgpro_metastore.

имя_пользователя и пароль: имя пользователя и пароль для подключения к серверу с каталогом pgpro_metastore.

Если какой-то из параметров отсутствует, например, при подключении к серверу с каталогом pgpro_metastore без пароля, вместо значения передайте пустую строку:

SELECT metastore.define_catalog_connection('хост','порт','имя_БД','','');

Чтобы изменения вступили в силу, повторно подключитесь к серверу pgpro_metastore (не к серверу с каталогом pgpro_metastore).

Если соединение с сервером с каталогом pgpro_metastore прервалось, повторное подключение произойдёт автоматически с использованием последних настроек параметров конфигурации:

Пример 26.1.

postgres=# SELECT metastore.remove_storage('mt_storage');
INFO:  Connection failed! Trying to reconnect.
INFO:  Reconnecting...1
INFO:  Reconnect successful!
        remove_storage
-----------------------------------
Storage was removed successfully.
(1 row)

Создайте каталог pgpro_metastore:

SELECT metastore.init(force_init bool DEFAULT false)

Если на сервере уже создан каталог pgpro_metastore, будет выведено предупреждение:

postgres=# SELECT metastore.init();
INFO:  Metastore catalog already initialized on remote server
INFO:  Run metastore.init(true) to force rewrite catalog on remote server
init
------

(1 row)

В этом случае каталог pgpro_metastore можно создать повторно. Для этого выполните тот же запрос, задав для параметра force_init значение true:

  postgres=# SELECT metastore.init(true);
  init
  ------

  (1 row)

Глава 27. Таблицы метаданных

Метаданные из таблиц метаданных pgpro_metastore необходимы для обеспечения следующей функциональности:

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

Запросы к аналитическим таблицам с секционированием и индексами для Parquet-файлов.

Поддержка базовых ETL-операций:

выполнение ETL-операций;

планирование ETL-операций на определённую дату и время, а также настройка последовательного выполнения (одной операции за другой);

ограничение ресурсов, доступных для выполнения ETL-операций, чтобы избежать увеличения OLTP-нагрузки.

Работа с OLAP-данными:

создание Parquet-файлов с OLAP-данными из таблиц Postgres Pro и импорт этих данных в аналитические таблицы;

экспорт OLAP-данных из аналитических таблиц в файлы в форматах Parquet, CSV и JSON;

создание отчётов с отфильтрованными OLAP-данными с помощью SQL-запросов.

Изменение объектов pgpro_metastore в рамках транзакций, а также согласованные изменения аналитических таблиц при чтении и записи.

Проверка целостности данных и восстановление Parquet-файлов из резервных копий или с помощью зеркал S3-хранилищ.

Мониторинг pgpro_metastore и уведомление администраторов о событиях, срочно требующих внимания.

Распределение Parquet-файлов по отдельным хранилищам во избежание конкуренции с OLTP-нагрузкой за пропускную способность ввода-вывода.

Схема таблиц метаданных представлена на диаграмме ниже.

27.1. Таблица метаданных pga_snapshot

В этой таблице метаданных содержится информация о снимках.

Имя столбца

Тип столбца

Ограничение

Описание

snapshot_id

BIGINT

Первичный ключ

Идентификатор снимка.

Значение увеличивается на единицу для каждого нового идентификатора.

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

snapshot_time

TIMESTAMP

NOT NULL

Дата и время создания снимка.

schema_version

BIGINT

NOT NULL

Версия каталога pgpro_metastore.

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

next_catalog_id

BIGINT

NOT NULL

Идентификатор, назначенный следующему объекту pgpro_metastore, например, аналитической таблице.

При создании объекта значение увеличивается.

next_file_id

BIGINT

NOT NULL

Идентификатор, назначаемый следующему созданному Parquet-файлу.

При создании файла значение увеличивается.

is_visible

BOOLEAN

NOT NULL

Определяет видимость объектов pgpro_metastore, которые ссылаются на снимок.

Возможные значения:

true

false

Когда в результате ETL-операции, например, add_table, создаётся несколько объектов, которые ссылаются на один и тот же снимок, для значения is_visible снимка задаётся false. Новые объекты не видны до завершения операции.

После этого значение is_visible меняется на true и объекты становятся видимыми, то есть доступными в выводе команды SELECT.

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

27.2. Таблица метаданных pga_snapshot_changes

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

Имя столбца

Тип столбца

Ограничение

Описание

snapshot_id

BIGINT

Первичный ключ, внешний ключ

Идентификатор снимка из таблицы метаданных pga_snapshot.

changes_made

VARCHAR

-

Список изменений снимка, разделённых запятыми, в следующих форматах:

created_schema:имя_схемы: снимок связан с созданием аналитической схемы.

created_table:имя_таблицы: снимок связан с созданием аналитической таблицы.

created_view:имя_представления: снимок связан с созданием представления Postgres Pro.

inserted_into_table:идентификатор_таблицы: снимок связан с добавлением OLAP-данных в аналитическую таблицу.

deleted_from_table:идентификатор_таблицы: снимок связан с удалением OLAP-данных из аналитической таблицы.

dropped_schema:идентификатор_схемы: снимок связан с удалением аналитической схемы.

dropped_table:идентификатор_таблицы: снимок связан с удалением аналитической таблицы.

dropped_view:идентификатор_представления: снимок связан с удалением представления Postgres Pro.

altered_table:идентификатор_таблицы: снимок связан с изменением метаданных аналитической таблицы.

altered_view:идентификатор_представления: снимок связан с изменением метаданных представления Postgres Pro.

27.3. Таблица метаданных pga_schema

В этой таблице метаданных содержится информация об аналитических схемах.

Имя столбца

Тип столбца

Ограничение

Описание

schema_id

BIGINT

Первичный ключ

Идентификатор аналитической схемы, назначаемый путём увеличения значения next_catalog_id таблицы метаданных pga_snapshot.

schema_uuid

UUID

NOT NULL, UNIQUE

Постоянный идентификатор аналитической схемы, сохраняемый для обеспечения совместимости со схемами Postgres Pro.

begin_snapshot

BIGINT

Внешний ключ, NOT NULL

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем создания аналитической схемы.

end_snapshot

BIGINT

Внешний ключ

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

Если значение NULL, аналитическая схема не удалена.

schema_name

VARCHAR

NOT NULL, UNIQUE

Уникальное имя аналитической схемы, например, my_schema.

27.4. Таблица метаданных pga_table

В этой таблице метаданных содержится информация об аналитических таблицах.

Имя столбца

Тип столбца

Ограничение

Описание

table_id

BIGINT

Первичный ключ

Идентификатор аналитической таблицы, назначаемый путём увеличения значения next_catalog_id таблицы метаданных pga_snapshot.

table_uuid

UUID

NOT NULL, UNIQUE

Постоянный идентификатор аналитической таблицы, сохраняемый для обеспечения совместимости с таблицами Postgres Pro.

begin_snapshot

BIGINT

Внешний ключ, NOT NULL

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем создания аналитической таблицы.

end_snapshot

BIGINT

Внешний ключ

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

Если значение NULL, аналитическая таблица не удалена.

schema_id

BIGINT

NOT NULL, UNIQUE

Идентификатор аналитической схемы из таблицы метаданных pga_schema, в которой создаётся аналитическая таблица.

table_name

VARCHAR

NOT NULL

Имя аналитической таблицы, например, my_table.

Значения table_name не UNIQUE (уникальные), поскольку pgpro_metastore не удаляет метаданные аналитических таблиц. Удалённые аналитические таблицы связываются со значениями end_snapshot, чтобы показать, что эти таблицы больше нельзя использовать.

Если бы значения table_name были UNIQUE (уникальные), создать аналитическую таблицу с тем же именем, что и у ранее удалённой таблицы, было бы невозможно.

path

VARCHAR

NOT NULL, UNIQUE

Путь к общему каталогу относительно значения uri таблицы метаданных pga_storage.

Абсолютный путь генерируется путём соединения значения uri таблицы метаданных pga_storage и значения path таблицы метаданных pga_table.

storage_id

BIGINT

Внешний ключ, NOT NULL

Идентификатор хранилища из таблицы метаданных pga_storage, в котором создана аналитическая таблица.

27.5. Таблица метаданных pga_storage

В этой таблице метаданных содержится информация о хранилищах.

Имя столбца

Тип столбца

Ограничение

Описание

storage_id

BIGINT

Первичный ключ

Идентификатор хранилища, назначаемый путём увеличения значения next_catalog_id таблицы метаданных pga_snapshot.

begin_snapshot

BIGINT

Внешний ключ, NOT NULL

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем создания хранилища.

end_snapshot

BIGINT

Внешний ключ

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем удаления хранилища.

Если значение NULL, хранилище не удалено.

storage_name

VARCHAR

NOT NULL

Имя хранилища, например, my_storage.

Значения storage_name не UNIQUE (уникальные), потому что pgpro_metastore не удаляет метаданные хранилища. Удалённые хранилища связываются со значениями end_snapshot, чтобы показать, что эти хранилища больше нельзя использовать.

Если бы значения storage_name были UNIQUE (уникальные), создать хранилище с тем же именем, что и у ранее удалённого хранилища, было бы невозможно.

uri

VARCHAR

NOT NULL, UNIQUE

URI корневого каталога хранилища.

У локальных хранилищ URI простой, например, file:///home/j.doe/workspace/axe/.

S3-хранилища отличаются более сложной структурой URI и логикой разбора, например, s3://premdb/team01.csv?versionId=w_B5qT8s5MkiT09.IRHay0lW.PycsHTS. Эти URI генерируются с помощью сторонних библиотек.

tmp_uri

VARCHAR

NOT NULL, UNIQUE

URI каталога хранилища для временных файлов.

У локальных хранилищ URI простой, например, file:///home/j.doe/workspace/axe/.

S3-хранилища отличаются более сложной структурой URI и логикой разбора, например, s3://premdb/team01.csv?versionId=w_B5qT8s5MkiT09.IRHay0lW.PycsHTS. Эти URI генерируются с помощью сторонних библиотек.

storage_type

BIGINT

NOT NULL

Тип хранилища.

В настоящее время поддерживаются локальные, сетевые и S3-хранилища.

min_space

BIGINT

NOT NULL

Минимально необходимое свободное место в хранилище, в байтах.

Проверяется при выполнении ETL-операции add_storage, а также других операций, если происходит удаление временных файлов из каталога, определённого значением tmp_uri.

uri_id

BIGINT

Внешний ключ, NOT NULL, UNIQUE

Идентификатор URI из таблицы метаданных pga_uri, связанный с корневым каталогом хранилища.

tmp_uri_id

BIGINT

Внешний ключ, NOT NULL, UNIQUE

Идентификатор URI из таблицы метаданных pga_uri, связанный с каталогом хранилища для временных файлов.

27.6. Таблица метаданных pga_uri

В этой таблице метаданных содержится системная информация о pgpro_metastore, и она не предназначена для изменения пользователями.

Столбец uri содержит исходные сериализованные URI, связанные со значениями uri или tmp_uri таблицы метаданных pga_storage.

Столбец path содержит пути к корневым каталогам хранилищ. Для локальных хранилищ значимы только значения этого столбца, которые, как правило, совпадают со значениями uri, за исключением префиксов, указывающих на тип хранилища:

file:// для локальных хранилищ;

s3:// для S3-хранилищ.

Остальные столбцы содержат произвольные URI для S3-хранилищ.

За подробной информацией обратитесь к RFS 3968 и к официальной документации AWS (ресурс недоступен на территории Российской Федерации).

27.7. Таблица метаданных pga_folder

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

Имя столбца

Тип столбца

Ограничение

Описание

folder_id

BIGINT

Первичный ключ

Идентификатор общего каталога, назначаемый путём увеличения значения next_catalog_id таблицы метаданных pga_snapshot.

begin_snapshot

BIGINT

Внешний ключ, NOT NULL

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем создания общего каталога.

end_snapshot

BIGINT

Внешний ключ

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем удаления общего каталога.

Если значение NULL, общий каталог не удалён.

path

VARCHAR

NOT NULL

Путь к общему каталогу относительно корневого каталога хранилища.

Полный путь получается путём соединения значения uri таблицы метаданных pga_storage и значения path таблицы метаданных pga_folder.

folder_name

VARCHAR

NOT NULL

Имя общего каталога, например, my_directory.

Значения folder_name не UNIQUE (уникальные), потому что pgpro_metastore не удаляет метаданные общих каталогов. Удалённые каталоги связываются со значениями end_snapshot, чтобы показать, что эти каталоги больше нельзя использовать.

Если бы значения folder_name были UNIQUE (уникальные), создать новый общий каталог с тем же именем, что и у ранее удалённого каталога, было бы невозможно.

storage_id

BIGINT

Внешний ключ, NOT NULL

Идентификатор хранилища из таблицы метаданных pga_storage, в котором создан общий каталог.

27.8. Таблица метаданных pga_column

В этой таблице метаданных содержится информация о столбцах аналитических таблиц.

Имя столбца

Тип столбца

Ограничение

Описание

column_id

BIGINT

Первичный ключ

Идентификатор столбца.

Если исходный Parquet-файл содержит идентификатор столбца field_id, этот идентификатор используется как значение column_id. Идентификатор меняется только при удалении столбца.

Значения column_id должны быть уникальными.

begin_snapshot

BIGINT

Внешний ключ, NOT NULL

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем создания столбца.

end_snapshot

BIGINT

Внешний ключ

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем удаления столбца.

Если значение NULL, столбец не удалён.

table_id

BIGINT

Внешний ключ, NOT NULL

Идентификатор аналитической таблицы из таблицы метаданных pga_table, в которой создан столбец.

column_order

BIGINT

NOT NULL

Порядковый номер столбца.

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

column_name

VARCHAR

NOT NULL

Имя столбца, например, my_column.

Значения column_name не UNIQUE (уникальные), потому что pgpro_metastore не удаляет метаданные столбцов. Удалённые столбцы связываются со значениями end_snapshot, чтобы показать, что эти столбцы больше нельзя использовать.

Если бы значения column_name были UNIQUE (уникальные), создать новый столбец с тем же именем, что и у ранее удалённого столбца, было бы невозможно.

column_type

VARCHAR

NOT NULL

Тип столбца, соответствующий одному из типов данных, поддерживаемых pgpro_metastore.

initial_default

VARCHAR

-

Исходное значение столбца, которое присваивается при создании столбца, например, после выполнения команды ALTER TABLE.

Может принимать значение NULL.

default_value

VARCHAR

-

Значение по умолчанию, используемое в операциях upsert.

Может принимать значение NULL.

nulls_allowed

BOOLEAN

-

Определяет, может ли столбец содержать значения NULL.

Возможные значения:

true

false

is_uniq

BOOLEAN

-

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

Возможные значения:

true

false

27.9. Таблица метаданных pga_data_file

В этой таблице метаданных содержится информация о Parquet-файлах.

Имя столбца

Тип столбца

Ограничение

Описание

data_file_id

BIGINT

Первичный ключ

Идентификатор Parquet-файла, назначаемый путём увеличения значения next_file_id таблицы метаданных pga_snapshot.

begin_snapshot

BIGINT

Внешний ключ, NOT NULL

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем создания Parquet-файла.

end_snapshot

BIGINT

Внешний ключ

Снимок из таблицы метаданных pga_snapshot, связанный с датой и временем удаления Parquet-файла.

Если значение NULL, Parquet-файл не удалён.

table_id

BIGINT

Внешний ключ, NOT NULL

Идентификатор аналитической таблицы из таблицы метаданных pga_table, связанный с Parquet-файлом.

file_order

BIGINT

NOT NULL

Вертикальная позиция в Parquet-файле.

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

path

VARCHAR

NOT NULL, UNIQUE

Путь к Parquet-файлу, например, my_file.parquet.

path_is_relative

BOOLEAN

NOT NULL

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

Возможные значения:

true

false

По умолчанию: false.

file_format

VARCHAR

NOT NULL

Формат файла.

На данный момент поддерживается только формат Parquet.

record_count

BIGINT

NOT NULL

Количество строк в Parquet-файле.

file_size_bytes

BIGINT

NOT NULL

Размер Parquet-файла, в байтах.

row_id_start

BIGINT

NOT NULL

Значение row_id для первой строки Parquet-файла. Обозначает начало Parquet-файла относительно таблицы.

checksum_crc32

BIGINT

NOT NULL

Текущее значение контрольной суммы Parquet-файла.

Используется для проверок целостности данных.

27.10. Таблица метаданных pga_files_scheduled_for_deletion

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

Имя столбца

Тип столбца

Ограничение

Описание

data_file_id

BIGINT

Первичный ключ, внешний ключ

Идентификатор Parquet-файла из таблицы метаданных pga_data_file.

path

VARCHAR

NOT NULL, UNIQUE

Путь к Parquet-файлу, например, my_file.parquet.

path_is_relative

BOOLEAN

NOT NULL

Указывает, является ли путь к Parquet-файлу относительным по отношению к пути к аналитической таблице или абсолютным.

Возможные значения:

true

false

По умолчанию: false.

schedule_start

TIMESTAMPTZ

NOT NULL

Дата и время запланированного удаления Parquet-файла.

27.11. Таблица метаданных pga_table_stats

В этой таблице метаданных содержится статистика аналитических таблиц.

Имя столбца

Тип столбца

Ограничение

Описание

table_id

BIGINT

Первичный ключ, внешний ключ

Идентификатор аналитической таблицы из таблицы метаданных pga_table.

record_count

BIGINT

NOT NULL

Количество строк в аналитической таблице.

next_row_id

BIGINT

NOT NULL

Значение row_id для следующего столбца, добавляемого в аналитическую таблицу.

file_size_bytes

BIGINT

NOT NULL

Общий размер всех Parquet-файлов, связанных с аналитической таблицей, в байтах.

27.12. Таблица метаданных pga_table_column_stats

В этой таблице метаданных содержится статистика по столбцам аналитических таблиц.

Имя столбца

Тип столбца

Ограничение

Описание

column_id

BIGINT

Первичный ключ, внешний ключ

Идентификатор столбца из таблицы метаданных pga_column.

contains_null

BOOLEAN

NOT NULL

Определяет, может ли столбец содержать значения NULL.

Возможные значения:

true

false

contains_nan

BOOLEAN

-

Определяет, может ли столбец содержать значения NaN.

Возможные значения:

true

false

Эта информация значима только для типов данных FLOAT, DOUBLE и NUMERIC.

min_value

VARCHAR

-

Минимальное значение в столбце.

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

max_value

VARCHAR

-

Максимальное значение в столбце.

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

27.13. Таблица метаданных pga_file_column_statistics

В этой таблице метаданных содержится статистика столбцов аналитических таблиц в разрезе Parquet-файлов.

Имя столбца

Тип столбца

Ограничение

Описание

data_file_id

BIGINT

Первичный ключ, внешний ключ

Идентификатор Parquet-файла из таблицы метаданных pga_data_file.

column_id

BIGINT

Первичный ключ, внешний ключ

Идентификатор столбца из таблицы метаданных pga_column.

column_size

BIGINT

NOT NULL

Размер столбца, в байтах.

value_count

BIGINT

NOT NULL

Количество элементов столбца, содержащихся в Parquet-файле.

Не равно количеству записей в Parquet-файле, так как в нём могут содержаться вложенные записи, такие как структуры и списки.

null_count

BIGINT

NOT NULL

Количество значений NULL в столбце.

min_value

VARCHAR

-

Минимальное значение для столбца в пределах Parquet-файла.

Значение можно использовать определения верхней границы минимального значения для столбца.

max_value

VARCHAR

-

Максимальное значение для столбца в пределах Parquet-файла.

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

27.14. Таблица метаданных pga_transaction_log

В этой таблице метаданных содержится информация по протоколированию задач pgpro_metastore, аналогичные стандартным транзакциям СУБД.

Имя столбца

Тип столбца

Ограничение

Описание

lsn

BIGSERIAL

Первичный ключ

Последовательный номер записи транзакции в журнале (LSN).

txid

BIGINT

NOT NULL

Идентификатор задачи.

io_id

INT

-

Идентификатор ETL-операции.

Одна задача может состоять из нескольких ETL-операций.

kind

VARCHAR

NOT NULL

Тип ETL-операции, например add_storage, add_table, add_files и create_view.

attr

JSONB

-

Данные, относящиеся к задаче: исходные и полученные в ходе её выполнения.

Глава 28. Чтение метаданных

28.1. Получение снимка

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

SELECT *
FROM pga_snapshot
WHERE snapshot_id = идентификатор_снимка;

Где snapshot_ID — это идентификатор снимка из таблицы метаданных pga_snapshot.

Чтобы получить последний снимок, выполните следующий запрос:

SELECT *
FROM pga_snapshot
WHERE snapshot_id =
  (SELECT max(snapshot_id) FROM pga_snapshot);

28.2. Вывод аналитических схем

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

SELECT schema_id, schema_name
FROM pga_schema
WHERE
    идентификатор_снимка >= begin_snapshot AND
    (идентификатор_снимка < end_snapshot OR end_snapshot IS NULL);

Где snapshot_ID — это идентификатор снимка из таблицы метаданных pga_snapshot.

28.3. Вывод аналитических таблиц

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

SELECT table_id, table_name
FROM pga_table
WHERE
    schema_id = идентификатор_схемы AND
    идентификатор_снимка >= begin_snapshot AND
    (идентификатор_снимка < end_snapshot OR end_snapshot IS NULL);

Где:

идентификатор_схемы: идентификатор аналитической схемы из таблицы метаданных pga_schema, в которой создаются аналитические таблицы.

идентификатор_снимка: идентификатор снимка из таблицы метаданных pga_snapshot.

28.4. Вывод столбцов аналитической таблицы

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

SELECT column_id, column_name, column_type
FROM pga_column
WHERE
    table_id = идентификатор_таблицы AND
    идентификатор_снимка >= begin_snapshot AND
    (идентификатор_снимка < end_snapshot OR end_snapshot IS NULL)
ORDER BY column_order;

Где:

идентификатор_таблицы: идентификатор аналитической таблицы из таблицы метаданных pga_table, в которой создаются столбцы.

идентификатор_снимка: идентификатор снимка из таблицы метаданных pga_snapshot.

28.5. Вывод отфильтрованных Parquet-файлов

Вывести Parquet-файлы можно после вывода столбцов аналитической таблицы. Полученные Parquet-файлы можно отфильтровать с помощью статистики из таблицы метаданных pga_file_column_statistics.

Чтобы вывести список Parquet-файлов и отфильтровать их по значениям столбцов, выполните следующий запрос:

SELECT data_file_id
FROM ducklake_file_column_stats
WHERE
    table_id = идентификатор_таблицы AND
    column_id = идентификатор_столбца AND
    (SCALAR >= min_value OR min_value IS NULL) AND
    (SCALAR <= max_value OR max_value IS NULL);

Где:

идентификатор_таблицы: идентификатор таблицы из таблицы метаданных pga_table, к которой относятся Parquet-файлы.

идентификатор_столбца: идентификатор столбца из таблицы метаданных pga_column, значения которого используются для фильтрации Parquet-файлов.

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

Чтобы отфильтровать значения столбцов, можно использовать разные условия, такие как > (больше чем), изменяя запрос соответствующим образом.

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

Глава 29. Настройка видимости объектов

Вы можете выполнять транзакционные ETL-операции над объектами pgpro_metastore. Для изоляции процедур, как и в Postgres Pro, используются snapshots. Снимки — это монотонно возрастающие последовательности чисел. У каждого объекта есть номер снимка, который связан с созданием и удалением этого объекта.

В ETL-операциях номера снимков используются для определения видимости объектов pgpro_metastore. По умолчанию для чтения текущего состояния pgpro_metastore используется номер последнего снимка. Например, можно запросить список Parquet-файлов аналитической таблицы, выполнив следующий запрос:

Пример 29.1.

  SELECT data.path AS data_file_path
  FROM ducklake_data_file AS data
  WHERE
      data.table_id = TABLE_ID AND
      SNAPSHOT_ID >= data.begin_snapshot AND
      (SNAPSHOT_ID < data.end_snapshot OR data.end_snapshot IS NULL)
  ORDER BY file_order;

Если в результате ETL-операции добавляется или удаляется объект pgpro_metastore, для него создаётся новый снимок и номер снимка указывается в качестве значений begin_snapshot или end_snapshot объекта в зависимости от типа операции.

Несмотря на то, что таблицы метаданных хранятся на сервере каталога pgpro_metastore, снимки каталога не связаны со снимками транзакций Postgres Pro. За счёт этого можно продолжить выполнение ETL-операции, даже если сервер с каталогом pgpro_metastore временно недоступен после перезагрузки. Недостатком использования независимых отдельных снимков является то, что для разрешения конфликтов, при которых два снимка изменяют один и тот же объект, требуется отдельный механизм.

pgpro_metastore также использует снимки сервера Postgres Pro, где выполняются ETL-операции. В настоящее время выполнение нескольких операций в одном блоке транзакции не поддерживается. Даже если исходные Postgres Pro-таблицы остаются согласованными в течение транзакции, они будут добавлены в pgpro_metastore в разных снимках.

29.1. Гарантии изоляции

С точки зрения изоляции выделятся два типа ETL-операций:

Выборка из аналитических таблиц.

Для таких операций используются стандартные представления Postgres Pro. На данный момент в результате чтения представления, связанного с аналитической таблицей, возвращается последняя версия данных даже для транзакций с уровнем изоляции Repeatable Read и выше. При этом видны только зафиксированные OLAP-данные.

Для обеспечения изоляции используются механизмы хранилища.

Другие ETL-операции с pgpro_metastore.

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

За подробной информацией обратитесь к Главе 15, Главе 16, Главе 19 и Главе 20.

29.2. Гарантии атомарности

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

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

  SELECT metastore.cleanup();

Часть VI. Приложения

Приложение A. Замечания к выпускам

Дата выпуска: 2026-01-30

Это первый публичный выпуск Postgres Pro AXE.

Решение предоставляет следующую функциональность:

Сценарии OLTP (online transaction processing, обработка транзакций в реальном времени) и OLAP (online analytical processing, интерактивная аналитическая обработка) с использованием СУБД Postgres Pro.

Векторизованные запросы к СУБД и использование модели столбцового хранения данных при работе с таблицами Postgres Pro и внешними хранилищами.

Построение хранилищ OLAP-данных.

Хранение OLAP-данных аналитических таблиц в виде Parquet-файлов.

Быстрый поиск и аналитика исторических данных.

Приложение B. Глоссарий

Аналитическая схема

Отдельное пространство имён для метаданных аналитических таблиц. Аналитические схемы похожи на схемы Postgres Pro.

Метаданные аналитических схем хранятся в таблице метаданных pga_schema.

За подробной информацией о работе с аналитическими схемами обратитесь к Главе 18.

Аналитическая таблица

Набор столбцов и строк с OLAP-данными, предоставляющий полную историю изменений данных и типа таблицы.

Строки аналитических таблиц хранятся в виде Parquet-файлов в хранилище. Метаданные аналитических таблиц хранятся в таблице метаданных pga_table.

За подробной информацией о работе с аналитическими таблицами обратитесь к Главе 16.

Тип аналитической таблицы

Упорядоченный набор столбцов аналитической таблицы, состоящий из их имён, типов и ограничений. Тип аналитической таблицы обновляется при работе со столбцами.

Представление аналитической таблицы

Представление Postgres Pro, которое предоставляет пользователям набор столбцов и строк аналитической таблицы. Представления используют метаданные аналитических таблиц каталога pgpro_metastore для эффективного выполнения аналитических запросов с отсечением секций (partition pruning) и проталкиванием предикатов (predicate pushdown).

За подробной информацией о создании представлений аналитических таблиц обратитесь к Разделу 16.3.

Операция выгрузки, преобразования и загрузки (Extract, Transform, Load — ETL)

Операция, которая выгружает, преобразовывает или загружает OLAP-данные аналитических таблиц. В настоящее время pgpro_metastore поддерживает следующие ETL-операции:

add_storage

remove_storage

add_table

rename_table

create_view

remove_table

add_column

rename_column

change_column_type

delete_column

add_schema

remove_schema

add_folder

remove_folder

add_files

copy_table

expire_snapshot

Таблица-куча

Стандартная таблица Postgres Pro.

Parquet

Бинарный формат файлов с открытым исходным кодом, разработанный для хранения и обработки больших объёмов данных. Данные организованы в виде столбцов, а не строк. Подобная столбцовая структура позволяет выполнять аналитические запросы быстрее, так как сканируются только требуемые столбцы, за счёт чего уменьшается объём обрабатываемых данных. Формат Parquet также поддерживает сжатие и кодирование для снижения размера файлов, например сжатие по столбцам, кодирование словаря, а также кодирование длин серий (Run-Lengh Encoding, RLE).

pgpro_metastore хранит OLAP-данные в виде Parquet-файлов.

Информация о Parquet-файлах хранится в таблицах метаданных pga_data_file, pga_files_scheduled_for_deletion и pga_file_partition_value.

За подробной информацией о формате Parquet обратитесь к https://parquet.apache.org.

Секция (партиция)

Группа Parquet-файлов, созданных на основании критериев секционирования, таких как определённые значения столбцов или диапазоны значений столбцов. Каждая аналитическая таблица имеет как минимум одну секцию. Дополнительные секции могут быть созданы одним из следующих способов:

автоматически при достижении лимита количества Parquet-файлов в секции;

на основании критериев секционирования.

Секционирование (партицирование)

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

В настоящее время поддерживается только hive-секционирование, предоставляемое DuckDB.

Каталог pgpro_metastore

Схема axe_catalog, в которой хранятся таблицы метаданных. Она может быть создана на сервере pgpro_metastore или на отдельном сервере.

Объекты pgpro_metastore

Основные сущности pgpro_metastore, к которым можно предоставлять права доступа:

хранилища;

аналитические таблицы;

общие каталоги.

Общий каталог

Каталог хранилища, в котором располагаются Parquet-файлы при добавлении или экспорте OLAP-данных из аналитических таблиц.

Метаданные общих каталогов хранятся в таблице метаданных pga_folder.

За подробной информацией об общих каталогах обратитесь к Главе 19.

Снимок

Сущность, которая поддерживает темпоральность pgpro_metastore за счёт ведения истории изменений OLAP-данных и метаданных. Снимки создаются в результате каждого действия пользователя и каждый снимок связан с одним изменением.

Конкретным примером использования снимков являются снимки аналитической таблицы. Они отображают состояние аналитической таблицы, включая её OLAP-данные, имя и тип на определённый момент времени. Аналитические таблицы изменяются в транзакциях, и для каждого изменения создаётся снимок. Вы можете восстановить любое состояние аналитической таблицы при условии, что её OLAP-данные не были удалены с помощью ETL-операции expire_snapshot.

Метаданные снимков хранятся в таблице метаданных pga_snapshot.

За примерами использования снимков обратитесь к Приложению E.

Хранилище

Физическое расположение Parquet-файлов и общих каталогов. Postgres Pro AXE поддерживает локальные, сетевые и S3-хранилища.

S3-хранилища (Simple Storage Service) — это облачные хранилища для любых типов данных, к которым можно обращаться через API. Данные хранятся в виде объектов внутри бакетов (контейнеров) с уникальными идентификаторами и метаданными, такими как тип, дата и время создания, а также права доступа. Основными преимуществами таких хранилищ являются их масштабируемость, гибкость и доступность из любой точки мира через Интернет.

Метаданные хранилищ хранятся в таблице метаданных pga_storage.

За подробной информацией о работе с хранилищами обратитесь к Главе 15.

Унифицированный идентификатор ресурса (Universal Resource Identifier — URI)

Строка подключения для хранилища, которая содержит информацию, необходимую для инициализации слоя хранения данных. Состоит из префикса подключения (например, 'file://', 's3://'), сетевого адреса, номера порта и пути. Дополнительно могут быть указаны имя S3-бакета, регион хранилища и протокол.

Приложение C. Результаты тестов производительности

В этом разделе содержатся результаты стандартных тестов производительности TPC-H, TPC-DS и ClickBench для pgpro_metastore.

Описание теста:

Размер данных: 100 ГБ. Соответствует коэффициенту масштабирования 100 в параметрах теста производительности.

Сервер: 64 виртуальных ядра и 128 ГБ оперативной памяти.

Ограничения:

Параметры конфигурации Postgres Pro не повлияли на результаты тестов производительности.

Для параметра конфигурации duckdb.max_memory было установлено значение 32Gb.

Для параметра конфигурации duckdb.threads было установлено значение 8.

Помимо времени выполнения запросов были измерены системные метрики, описанные в таблице ниже.

Метрика

Единицы измерения

Описание

user time

Секунды

Количество секунд, в течение которых ЦП использовался процессом в режиме пользователя

sys time

Секунды

Количество секунд, в течение которых ЦП использовался процессом в режиме ядра

max memory

МБ

Максимальный объём памяти, используемый при выполнении процесса

avg memory

МБ

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

read IO

МБ

Объём данных, прочитанных из хранилища при выполнении процесса

write IO

МБ

Объём данных, записанных в хранилище при выполнении процесса

Подготовка тестовых данных и таблиц:

Тестовые данные сгенерированы в формате CSV.

За подробной информацией об используемых инструментах и процедуре в целом обратитесь к https://tpc.org.

CSV-файлы загружены в таблицы-кучи.

Тестовые данные были экспортированы в Parquet-файлы, после чего для них были созданы аналитические представления.

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

C.1. Тест производительности TPC-H

C.1.1. Создание аналитических таблиц для теста производительности TPC-H

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

COPY nation
TO '/home/test/tpch_tables100/nation.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY region
TO '/home/test/tpch_tables100/region.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY part
TO '/home/test/tpch_tables100/part.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY supplier
TO '/home/test/tpch_tables100/supplier.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY partsupp
TO '/home/test/tpch_tables100/partsupp.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY customer
TO '/home/test/tpch_tables100/customer.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY orders
TO '/home/test/tpch_tables100/orders.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY lineitem
TO '/home/test/tpch_tables100/lineitem.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

CREATE VIEW nation_parquet AS SELECT
r['n_nationkey']::integer AS n_nationkey,
r['n_name']::text AS n_name,0
r['n_regionkey']::integer AS n_regionkey,
r['n_comment']::text AS n_comment FROM
read_parquet('/home/test/tpch_tables100/nation.parquet', binary_as_string => true) r;

CREATE VIEW region_parquet AS SELECT
r['r_regionkey']::integer AS r_regionkey,
r['r_name']::text AS r_name,
r['r_comment']::text AS r_comment FROM
read_parquet('/home/test/tpch_tables100/region.parquet', binary_as_string => true) r;

CREATE VIEW part_parquet AS SELECT
r['p_partkey']::integer AS p_partkey,
r['p_name']::text AS p_name,
r['p_mfgr']::text AS p_mfgr,
r['p_brand']::text AS p_brand,
r['p_type']::text AS p_type,
r['p_size']::integer AS p_size,
r['p_container']::text AS p_container,
r['p_retailprice']::decimal(15,2) AS p_retailprice,
r['p_comment']::text AS p_comment FROM
read_parquet('/home/test/tpch_tables100/part.parquet', binary_as_string => true) r;

CREATE VIEW supplier_parquet AS SELECT
r['s_suppkey']::integer AS s_suppkey,
r['s_name']::text AS s_name,
r['s_address']::text AS s_address,
r['s_nationkey']::integer AS s_nationkey,
r['s_phone']::text AS s_phone,
r['s_acctbal']::decimal(15,2) AS s_acctbal,
r['s_comment']::text AS s_comment FROM
read_parquet('/home/test/tpch_tables100/supplier.parquet', binary_as_string => true) r;

CREATE VIEW nation_parquet AS SELECT
r['n_nationkey']::integer AS n_nationkey,
r['n_name']::text AS n_name,
r['n_regionkey']::integer AS n_regionkey,
r['n_comment']::text AS n_comment FROM
read_parquet('/home/test/tpch_tables100/nation.parquet', binary_as_string => true) r;

CREATE VIEW region_parquet AS SELECT
r['r_regionkey']::integer AS r_regionkey,
r['r_name']::text AS r_name,
r['r_comment']::text AS r_comment FROM
read_parquet('/home/test/tpch_tables100/region.parquet', binary_as_string => true) r;

CREATE VIEW part_parquet AS SELECT
r['p_partkey']::integer AS p_partkey,
r['p_name']::text AS p_name,
r['p_mfgr']::text AS p_mfgr,
r['p_brand']::text AS p_brand,
r['p_type']::text AS p_type,
r['p_size']::integer AS p_size,
r['p_container']::text AS p_container,
r['p_retailprice']::decimal(15,2) AS p_retailprice,
r['p_comment']::text AS p_comment FROM
read_parquet('/home/test/tpch_tables100/part.parquet', binary_as_string => true) r;

CREATE VIEW supplier_parquet as select
r['s_suppkey']::integer as s_suppkey,
r['s_name']::text as s_name,
r['s_address']::text as s_address,
r['s_nationkey']::integer as s_nationkey,
r['s_phone']::text as s_phone,
r['s_acctbal']::decimal(15,2) as s_acctbal,
r['s_comment']::text as s_comment from
read_parquet('/home/test/tpch_tables100/supplier.parquet', binary_as_string => true) r;

CREATE VIEW lineitem_parquet AS SELECT
r['l_orderkey']::integer AS l_orderkey,
r['l_partkey']::integer AS l_partkey,
r['l_suppkey']::integer AS l_suppkey,
r['l_linenumber']::integer AS l_linenumber,
r['l_quantity']::decimal(15,2) AS l_quantity,
r['l_extendedprice']::decimal(15,2) AS l_extendedprice,
r['l_discount']::decimal(15,2) AS l_discount,
r['l_tax']::decimal(15,2) AS l_tax,
r['l_returnflag']::text AS l_returnflag,
r['l_linestatus']::text AS l_linestatus,
r['l_shipdate']::date AS l_shipdate,
r['l_commitdate']::date AS l_commitdate,
r['l_receiptdate']::date AS l_receiptdate,
r['l_shipinstruct']::text AS l_shipinstruct,
r['l_shipmode']::text AS l_shipmode,
r['l_comment']::text AS l_comment FRO
read_parquet('/home/test/tpch_tables100/lineitem.parquet', binary_as_string => true) r;

C.1.2. Тест производительности TPC-H: коэффициент масштабирования 10

Таблица C.1. Тестовые данные

Данные

Общий размер

Аналитические таблицы в формате CSV

12.3 ГБ

Аналитические таблицы в формате Parquet

2.3 ГБ

Таблица C.2. Информация о таблице

Аналитическая таблица

Количество строк

customer

1 500 000

lineitem

60 000 000

nation

25

orders

15 000 000

part

2 000 000

partsupp

8 000 000

region

5

supplier

100 000

Таблица C.3. Результаты теста

Запрос

Продолжительность (сек)

user time (сек)

sys time (сек)

max RSS (МБ)

avg RSS (МБ)

read IO (МБ)

write IO (МБ)

Q1

1.24

4.41

0.49

175.78

119.21

547.41

0.00

Q1 — второе выполнение

0.64

3.97

0.13

28.99

19.21

0.00

0.00

Q2

0.41

0.35

0.14

137.95

55.49

125.65

0.00

Q2 — второе выполнение

0.16

0.65

0.08

87.58

87.58

0.12

0.00

Q3

1.16

2.83

0.73

207.54

149.16

859.54

0.00

Q3 — второе выполнение

0.43

2.55

0.24

79.77

75.65

0.00

0.00

Q4

0.75

1.54

0.38

160.36

96.14

372.92

0.00

Q4 — второе выполнение

0.34

1.46

0.14

94.09

94.03

0.00

0.00

Q5

1.10

2.71

0.65

150.69

86.55

816.84

0.00

Q5 — второе выполнение

0.46

2.58

0.20

61.12

52.95

0.00

0.00

Q6

0.70

1.06

0.41

138.09

83.34

429.32

0.00

Q6 — второе выполнение

0.23

0.73

0.09

4.45

4.45

0.00

0.00

Q7

1.15

2.35

0.73

107.64

56.96

778.34

0.00

Q7 — второе выполнение

0.44

2.64

0.25

138.61

92.78

0.00

0.00

Q8

1.26

3.29

0.78

111.01

70.97

891.59

0.00

Q8 — второе выполнение

0.54

2.48

0.18

132.82

113.96

0.00

0.00

Q9

1.56

5.75

1.33

1187.36

844.31

998.59

0.00

Q9 — второе выполнение

0.96

5.41

0.84

1016.37

856.13

0.00

0.00

Q10

1.29

2.69

0.92

279.62

91.94

897.04

0.00

Q10 — второе выполнение

0.53

2.41

0.34

294.97

144.50

0.00

0.00

Q11

0.74

0.88

0.14

60.24

27.92

113.06

0.00

Q11 — второе выполнение

0.58

0.83

0.08

75.74

69.54

0.00

0.00

Q12

0.78

1.64

0.48

115.89

66.10

554.29

0.00

Q12 — второе выполнение

0.30

1.61

0.14

58.46

53.79

0.00

0.00

Q13

1.05

5.64

0.57

566.08

323.92

384.95

0.00

Q13 — второе выполнение

0.81

5.08

0.37

424.62

304.15

0.00

0.00

Q14

1.21

1.77

0.85

144.30

91.96

982.34

0.00

Q14 — второе выполнение

0.30

1.65

0.22

169.76

169.08

0.00

0.00

Q15

1.14

2.21

0.68

116.00

75.72

681.02

0.00

Q15 — второе выполнение

0.49

2.43

0.27

130.55

108.68

0.06

0.00

Q16

0.35

0.76

0.20

226.43

99.28

105.09

0.00

Q16 — второе выполнение

0.22

0.68

0.16

220.67

220.67

0.00

0.00

Q17

1.19

2.06

0.58

45.12

3.68

928.13

0.00

Q17 — второе выполнение

0.38

1.46

0.14

25.31

12.52

0.00

0.00

Q18

0.99

3.22

0.75

854.52

449.22

332.49

0.00

Q18 — второе выполнение

0.65

3.20

0.54

817.68

559.04

0.00

0.00

Q19

1.35

5.36

0.78

248.18

147.08

1047.49

0.00

Q19 — второе выполнение

0.77

4.96

0.25

113.11

111.55

0.00

0.00

Q20

1.08

1.53

0.73

94.06

33.74

830.65

0.00

Q20 — второе выполнение

0.29

1.48

0.18

82.06

78.86

0.00

0.00

Q21

1.72

6.05

0.82

412.80

283.63

742.20

0.00

Q21 — второе выполнение

1.14

6.59

0.45

425.94

322.94

0.00

0.00

Q22

0.36

0.68

0.17

144.75

63.07

140.62

0.00

Q22 — второе выполнение

0.15

0.00

0.00

0.00

0.00

0.00

0.00

C.1.3. Тест производительности TPC-H: коэффициент масштабирования 100

Таблица C.4. Тестовые данные

Данные

Общий размер

Аналитические таблицы в формате CSV

112.7 ГБ

Аналитические таблицы в формате Parquet

24.3 ГБ

База данных

128 ГБ

Таблица C.5. Информация о таблице

Аналитическая таблица

Количество строк

customer

15 000 000

lineitem

600 000 000

nation

25

orders

150 000 000

part

20 000 000

partsupp

80 000 000

region

5

supplier

1 000 000

Таблица C.6. Результаты теста

Запрос

Продолжительность (сек)

user time (сек)

sys time (сек)

max RSS (МБ)

avg RSS (МБ)

read IO (МБ)

write IO (МБ)

Q1

12.92

72.80

4.05

492.73

311.95

6358.24

0.00

Q1 — второе выполнение

9.46

70.33

1.16

409.30

273.64

0.00

0.00

Q2

2.12

6.27

0.96

491.55

361.52

1321.13

0.00

Q2 — второе выполнение

1.03

6.48

0.40

354.27

302.37

0.00

0.00

Q3

11.38

33.43

5.69

934.96

765.60

9683.71

0.00

Q3 — второе выполнение

4.58

31.15

1.31

875.82

686.64

0.00

0.00

Q4

8.10

36.50

5.93

4775.98

3691.67

4396.98

0.00

Q4 — второе выполнение

2.77

17.54

0.86

839.92

641.36

0.00

0.00

Q5

10.74

40.22

5.07

656.58

521.63

9477.89

0.00

Q5 — второе выполнение

5.38

37.76

1.17

645.07

531.99

0.00

0.00

Q6

7.02

12.20

3.64

265.81

207.99

6284.00

0.00

Q6 — второе выполнение

1.89

10.53

0.76

263.79

182.32

0.00

0.00

Q7

11.26

31.34

5.93

1247.71

606.35

9093.37

0.00

Q7 — второе выполнение

4.28

27.75

1.63

1439.95

794.07

0.00

0.00

Q8

12.48

45.23

5.87

909.35

707.38

9528.32

0.00

Q8 — второе выполнение

6.08

42.93

1.57

784.45

639.46

0.00

0.00

Q9

16.47

81.05

12.55

12589.41

10355.32

11261.07

0.00

Q9 — второе выполнение

11.66

77.63

8.09

12392.88

10138.04

0.00

0.00

Q10

11.97

30.71

7.86

2207.39

680.45

10548.94

0.00

Q10 — второе выполнение

4.34

27.91

2.14

1841.71

635.12

0.00

0.00

Q11

1.78

6.22

0.84

209.13

110.15

978.71

0.00

Q11 — второе выполнение

1.04

5.98

0.37

201.26

128.80

0.00

0.00

Q12

6.79

17.32

3.98

326.39

179.29

5353.07

0.00

Q12 — второе выполнение

2.57

15.04

0.92

406.32

248.50

0.00

0.00

Q13

8.29

54.70

4.03

4087.91

2248.88

3647.88

0.00

Q13 — второе выполнение

7.36

55.07

2.86

3989.38

2200.67

0.00

0.00

Q14

12.22

16.23

8.04

1672.34

1471.48

11552.39

0.00

Q14 — второе выполнение

2.51

14.48

1.80

1805.81

1419.27

0.00

0.00

Q15

9.60

25.58

5.90

878.58

453.89

7232.63

0.02

Q15 — второе выполнение

4.11

23.70

2.20

1280.88

789.34

0.06

0.00

Q16

1.47

6.80

1.19

1471.02

803.93

798.05

0.00

Q16 — второе выполнение

1.05

6.62

0.83

1352.21

890.62

0.00

0.00

Q17

11.55

36.66

4.95

323.85

103.95

9784.29

0.00

Q17 — второе выполнение

5.38

35.37

1.19

347.43

274.43

0.00

0.00

Q18

10.20

50.57

7.40

7231.00

3504.42

3183.46

0.00

Q18 — второе выполнение

8.03

48.98

5.96

6903.64

3178.65

0.00

0.00

Q19

12.86

55.66

6.89

1300.25

845.86

11058.48

0.00

Q19 — второе выполнение

7.77

55.12

2.43

1625.37

1393.91

0.00

0.00

Q20

9.83

16.10

5.21

390.94

302.91

8856.13

0.00

Q20 — второе выполнение

2.41

14.04

1.03

374.55

260.89

0.00

0.00

Q21

14.97

66.87

8.54

4101.62

2792.17

7644.21

0.00

Q21 — второе выполнение

10.46

64.93

4.72

3951.81

2257.96

0.00

0.00

Q22

1.97

9.47

0.95

237.94

134.41

1340.10

0.00

Q22 — второе выполнение

1.33

9.05

0.34

243.36

181.38

0.00

0.00

C.2. Тест производительности TCP-DS

C.2.1. Создание аналитических таблиц для теста производительности TPC-DS

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

COPY dbgen_version
TO '/home/test/tpcds_tables100/dbgen_version.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY customer_address
TO '/home/test/tpcds_tables100/customer_address.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY customer_demographics
TO '/home/test/tpcds_tables100/customer_demographics.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY date_dim
TO '/home/test/tpcds_tables100/date_dim.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY warehouse
TO '/home/test/tpcds_tables100/warehouse.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY ship_mode
TO '/home/test/tpcds_tables100/ship_mode.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY time_dim
TO '/home/test/tpcds_tables100/time_dim.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY reason
TO '/home/test/tpcds_tables100/reason.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY income_band
TO '/home/test/tpcds_tables100/income_band.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY item
TO '/home/test/tpcds_tables100/item.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY store
TO '/home/test/tpcds_tables100/store.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY call_center
TO '/home/test/tpcds_tables100/call_center.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY customer
TO '/home/test/tpcds_tables100/customer.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY web_site
TO '/home/test/tpcds_tables100/web_site.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY store_returns
TO '/home/test/tpcds_tables100/store_returns.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY household_demographics
TO '/home/test/tpcds_tables100/household_demographics.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY web_page
TO '/home/test/tpcds_tables100/web_page.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY promotion
TO '/home/test/tpcds_tables100/promotion.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY catalog_page
TO '/home/test/tpcds_tables100/catalog_page.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY inventory
TO '/home/test/tpcds_tables100/inventory.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY catalog_returns
TO '/home/test/tpcds_tables100/catalog_returns.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY web_returns
TO '/home/test/tpcds_tables100/web_returns.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY web_sales
TO '/home/test/tpcds_tables100/web_sales.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY catalog_sales
TO '/home/test/tpcds_tables100/catalog_sales.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

COPY store_sales
TO '/home/test/tpcds_tables100/store_sales.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

CREATE VIEW dbgen_version_parquet AS SELECT
  r['dv_version']::text AS dv_version,
  r['dv_create_date']::date AS dv_create_date,
  r['dv_create_time']::time AS dv_create_time,
  r['dv_cmdline_args']::text AS dv_cmdline_args FROM
read_parquet('/home/test/tpcds_tables100/dbgen_version.parquet', binary_as_string => true) r;

CREATE VIEW customer_address_parquet AS SElECT
  r['ca_address_sk']::integer AS ca_address_sk,
  r['ca_address_id']::text AS ca_address_id,
  r['ca_street_number']::text AS ca_street_number,
  r['ca_street_name']::text AS ca_street_name,
  r['ca_street_type']::text AS ca_street_type,
  r['ca_suite_number']::text AS ca_suite_number,
  r['ca_city']::text AS ca_city,
  r['ca_county']::text AS ca_county,
  r['ca_state']::text AS ca_state,
  r['ca_zip']::text AS ca_zip,
  r['ca_country']::text AS ca_country,
  r['ca_gmt_offset']::decimal(5,2) AS ca_gmt_offset,
  r['ca_location_type']::text AS ca_location_type FROM
read_parquet('/home/test/tpcds_tables100/customer_address.parquet', binary_as_string => true) r;

CREATE VIEW customer_demographics_parquet AS SELECT
  r['cd_demo_sk']::integer AS cd_demo_skz
  r['cd_gender']::text AS cd_gender,
  r['cd_marital_status']::text AS cd_marital_status,
  r['cd_education_status']::text AS cd_education_status,
  r['cd_purchase_estimate']::integer AS cd_purchase_estimate,
  r['cd_credit_rating']::text AS cd_credit_rating,
  r['cd_dep_count']::integer AS cd_dep_count,
  r['cd_dep_employed_count']::integer AS cd_dep_employed_count,
  r['cd_dep_college_count']::integer AS cd_dep_college_count FROM
read_parquet('/home/test/tpcds_tables100/customer_demographics.parquet', binary_as_string => true) r;

CREATE VIEW date_dim_parquet AS SELECT
  r['d_date_sk']::integer AS d_date_sk,
  r['d_date_id']::text AS d_date_id,
  r['d_date']::date AS d_date,
  r['d_month_seq']::integer AS d_month_seq,
  r['d_week_seq']::integer AS d_week_seq,
  r['d_quarter_seq']::integer AS d_quarter_seq,
  r['d_year']::integer AS d_year,
  r['d_dow']::integer AS d_dow,
  r['d_moy']::integer AS d_moy,
  r['d_dom']::integer AS d_dom,
  r['d_qoy']::integer AS d_qoy,
  r['d_fy_year']::integer AS d_fy_year,
  r['d_fy_quarter_seq']::integer AS d_fy_quarter_seq,
  r['d_fy_week_seq']::integer AS d_fy_week_seq,
  r['d_day_name']::text AS d_day_name,
  r['d_quarter_name']::text AS d_quarter_name,
  r['d_holiday']::text AS d_holiday,
  r['d_weekend']::text AS d_weekend,
  r['d_following_holiday']::text AS d_following_holiday,
  r['d_first_dom']::integer AS d_first_dom,
  r['d_last_dom']::integer AS d_last_dom,
  r['d_same_day_ly']::integer AS d_same_day_ly,
  r['d_same_day_lq']::integer AS d_same_day_lq,
  r['d_current_day']::text AS d_current_day,
  r['d_current_week']::text AS d_current_week,
  r['d_current_month']::text AS d_current_month,
  r['d_current_quarter']::text AS d_current_quarter,
  r['d_current_year']::text AS d_current_year FROM
read_parquet('/home/test/tpcds_tables100/date_dim.parquet', binary_as_string => true) r;

CREATE VIEW warehouse_parquet AS SELECT
  r['w_warehouse_sk']::integer AS w_warehouse_sk,
  r['w_warehouse_id']::text AS w_warehouse_id,
  r['w_warehouse_name']::text AS w_warehouse_name,
  r['w_warehouse_sq_ft']::integer AS w_warehouse_sq_ft,
  r['w_street_number']::text AS w_street_number,
  r['w_street_name']::text AS w_street_name,
  r['w_street_type']::text AS w_street_type,
  r['w_suite_number']::text AS w_suite_number,
  r['w_city']::text AS w_city,
  r['w_county']::text AS w_county,
  r['w_state']::text AS w_state,
  r['w_zip']::text AS w_zip,
  r['w_country']::text AS w_country,
  r['w_gmt_offset']::decimal(5,2) AS w_gmt_offset FROM
read_parquet('/home/test/tpcds_tables100/warehouse.parquet', binary_as_string => true) r;

CREATE VIEW ship_mode_parquet AS SELECT
  r['sm_ship_mode_sk']::integer AS sm_ship_mode_sk,
  r['sm_ship_mode_id']::text AS sm_ship_mode_id,
  r['sm_type']::text AS sm_type,
  r['sm_code']::text AS sm_code,
  r['sm_carrier']::text AS sm_carrier,
  r['sm_contract']::text AS sm_contract FROM
read_parquet('/home/test/tpcds_tables100/ship_mode.parquet', binary_as_string => true) r;

CREATE VIEW time_dim_parquet AS SELECT
  r['t_time_sk']::integer AS t_time_sk,
  r['t_time_id']::text AS t_time_id,
  r['t_time']::integer AS t_time,
  r['t_hour']::integer AS t_hour,
  r['t_minute']::integer AS t_minute,
  r['t_second']::integer AS t_second,
  r['t_am_pm']::text AS t_am_pm,
  r['t_shift']::text AS t_shift,
  r['t_sub_shift']::text AS t_sub_shift,
  r['t_meal_time']::text AS t_meal_time FROM
read_parquet('/home/test/tpcds_tables100/time_dim.parquet', binary_as_string => true) r;

CREATE VIEW reason_parquet AS SELECT
  r['r_reason_sk']::integer AS r_reason_sk,
  r['r_reason_id']::text AS r_reason_id,
  r['r_reason_desc']::text AS r_reason_desc FROM
read_parquet('/home/test/tpcds_tables100/reason.parquet', binary_as_string => true) r;

CREATE VIEW income_band_parquet AS SELECT
  r['ib_income_band_sk']::integer AS ib_income_band_sk,
  r['ib_lower_bound']::integer AS ib_lower_bound,
  r['ib_upper_bound']::integer AS ib_upper_bound FROM
read_parquet('/home/test/tpcds_tables100/income_band.parquet', binary_as_string => true) r;

CREATE VIEW item_parquet AS SELECT
  r['i_item_sk']::integer AS i_item_sk,
  r['i_item_id']::text AS i_item_id,
  r['i_rec_start_date']::date AS i_rec_start_date,
  r['i_rec_end_date']::date AS i_rec_end_date,
  r['i_item_desc']::text AS i_item_desc,
  r['i_current_price']::decimal(7,2) AS i_current_price,
  r['i_wholesale_cost']::decimal(7,2) AS i_wholesale_cost,
  r['i_brand_id']::integer AS i_brand_id,
  r['i_brand']::text AS i_brand,
  r['i_class_id']::integer AS i_class_id,
  r['i_class']::text AS i_class,
  r['i_category_id']::integer AS i_category_id,
  r['i_category']::text AS i_category,
  r['i_manufact_id']::integer AS i_manufact_id,
  r['i_manufact']::text AS i_manufact,
  r['i_size']::text AS i_size,
  r['i_formulation']::text AS i_formulation,
  r['i_color']::text AS i_color,
  r['i_units']::text AS i_units,
  r['i_container']::text AS i_container,
  r['i_manager_id']::integer AS i_manager_id,
  r['i_product_name']::text AS i_product_name FROM
read_parquet('/home/test/tpcds_tables100/item.parquet', binary_as_string => true) r;

create view store_parquet AS SELECT
  r['s_store_sk']::integer AS s_store_sk,
  r['s_store_id']::text AS s_store_id,
  r['s_rec_start_date']::date AS s_rec_start_date,
  r['s_rec_end_date']::date AS s_rec_end_date,
  r['s_closed_date_sk']::integer AS s_closed_date_sk,
  r['s_store_name']::text AS s_store_name,
  r['s_number_employees']::integer AS s_number_employees,
  r['s_floor_space']::integer AS s_floor_space,
  r['s_hours']::text AS s_hours,
  r['s_manager']::text AS s_manager,
  r['s_market_id']::integer AS s_market_id,
  r['s_geography_class']::text AS s_geography_class,
  r['s_market_desc']::text AS s_market_desc,
  r['s_market_manager']::text AS s_market_manager,
  r['s_division_id']::integer AS s_division_id,
  r['s_division_name']::text AS s_division_name,
  r['s_company_id']::integer AS s_company_id,
  r['s_company_name']::text AS s_company_name,
  r['s_street_number']::text AS s_street_number,
  r['s_street_name']::text AS s_street_name,
  r['s_street_type']::text AS s_street_type,
  r['s_suite_number']::text AS s_suite_number,
  r['s_city']::text AS s_city,
  r['s_county']::text AS s_county,
  r['s_state']::text AS s_state,
  r['s_zip']::text AS s_zip,
  r['s_country']::text AS s_country,
  r['s_gmt_offset']::decimal(5,2) AS s_gmt_offset,
  r['s_tax_precentage']::decimal(5,2) AS s_tax_precentage FROM
read_parquet('/home/test/tpcds_tables100/store.parquet', binary_as_string => true) r;

CREATE VIEW call_center_parquet AS SELECT
  r['cc_call_center_sk']::integer AS cc_call_center_sk,
  r['cc_call_center_id']::text AS cc_call_center_id,
  r['cc_rec_start_date']::date AS cc_rec_start_date,
  r['cc_rec_end_date']::date AS cc_rec_end_date,
  r['cc_closed_date_sk']::integer AS cc_closed_date_sk,
  r['cc_open_date_sk']::integer AS cc_open_date_sk,
  r['cc_name']::text AS cc_name,
  r['cc_class']::text AS cc_class,
  r['cc_employees']::integer AS cc_employees,
  r['cc_sq_ft']::integer AS cc_sq_ft,
  r['cc_hours']::text AS cc_hours,
  r['cc_manager']::text AS cc_manager,
  r['cc_mkt_id']::integer AS cc_mkt_id,
  r['cc_mkt_class']::text AS cc_mkt_class,
  r['cc_mkt_desc']::text AS cc_mkt_desc,
  r['cc_market_manager']::text AS cc_market_manager,
  r['cc_division']::integer AS cc_division,
  r['cc_division_name']::text AS cc_division_name,
  r['cc_company']::integer AS cc_company,
  r['cc_company_name']::text AS cc_company_name,
  r['cc_street_number']::text AS cc_street_number,
  r['cc_street_name']::text AS cc_street_name,
  r['cc_street_type']::text AS cc_street_type,
  r['cc_suite_number']::text AS cc_suite_number,
  r['cc_city']::text AS cc_city,
  r['cc_county']::text AS cc_county,
  r['cc_state']::text AS cc_state,
  r['cc_zip']::text AS cc_zip,
  r['cc_country']::text AS cc_country,
  r['cc_gmt_offset']::decimal(5,2) AS cc_gmt_offset,
  r['cc_tax_percentage']::decimal(5,2) AS cc_tax_percentage FROM
read_parquet('/home/test/tpcds_tables100/call_center.parquet', binary_as_string => true) r;

CREATE VIEW customer_parquet AS SELECT
  r['c_customer_sk']::integer AS c_customer_sk,
  r['c_customer_id']::text AS c_customer_id,
  r['c_current_cdemo_sk']::integer AS c_current_cdemo_sk,
  r['c_current_hdemo_sk']::integer AS c_current_hdemo_sk,
  r['c_current_addr_sk']::integer AS c_current_addr_sk,
  r['c_first_shipto_date_sk']::integer AS c_first_shipto_date_sk,
  r['c_first_sales_date_sk']::integer AS c_first_sales_date_sk,
  r['c_salutation']::text AS c_salutation,
  r['c_first_name']::text AS c_first_name,
  r['c_last_name']::text AS c_last_name,
  r['c_preferred_cust_flag']::text AS c_preferred_cust_flag,
  r['c_birth_day']::integer AS c_birth_day,
  r['c_birth_month']::integer AS c_birth_month,
  r['c_birth_year']::integer AS c_birth_year,
  r['c_birth_country']::text AS c_birth_country,
  r['c_login']::text AS c_login,
  r['c_email_address']::text AS c_email_address,
  r['c_last_review_date_sk']::integer AS c_last_review_date_sk FROM
read_parquet('/home/test/tpcds_tables100/customer.parquet', binary_as_string => true) r;

CREATE VIEW web_site_parquet AS SELECT
  r['web_site_sk']::integer AS web_site_sk,
  r['web_site_id']::text AS web_site_id,
  r['web_rec_start_date']::date AS web_rec_start_date,
  r['web_rec_end_date']::date AS web_rec_end_date,
  r['web_name']::text AS web_name,
  r['web_open_date_sk']::integer AS web_open_date_sk,
  r['web_close_date_sk']::integer AS web_close_date_sk,
  r['web_class']::text AS web_class,
  r['web_manager']::text AS web_manager,
  r['web_mkt_id']::integer AS web_mkt_id,
  r['web_mkt_class']::text AS web_mkt_class,
  r['web_mkt_desc']::text AS web_mkt_desc,
  r['web_market_manager']::text AS web_market_manager,
  r['web_company_id']::integer AS web_company_id,
  r['web_company_name']::text AS web_company_name,
  r['web_street_number']::text AS web_street_number,
  r['web_street_name']::text AS web_street_name,
  r['web_street_type']::text AS web_street_type,
  r['web_suite_number']::text AS web_suite_number,
  r['web_city']::text AS web_city,
  r['web_county']::text AS web_county,
  r['web_state']::text AS web_state,
  r['web_zip']::text AS web_zip,
  r['web_country']::text AS web_country,
  r['web_gmt_offset']::decimal(5,2) AS web_gmt_offset,
  r['web_tax_percentage']::decimal(5,2) AS web_tax_percentage FROM
read_parquet('/home/test/tpcds_tables100/web_site.parquet', binary_as_string => true) r;

CREATE VIEW store_returns_parquet AS SELECT
  r['sr_returned_date_sk']::integer AS sr_returned_date_sk,
  r['sr_return_time_sk']::integer AS sr_return_time_sk,
  r['sr_item_sk']::integer AS sr_item_sk,
  r['sr_customer_sk']::integer AS sr_customer_sk,
  r['sr_cdemo_sk']::integer AS sr_cdemo_sk,
  r['sr_hdemo_sk']::integer AS sr_hdemo_sk,
  r['sr_addr_sk']::integer AS sr_addr_sk,
  r['sr_store_sk']::integer AS sr_store_sk,
  r['sr_reason_sk']::integer AS sr_reason_sk,
  r['sr_ticket_number']::integer AS sr_ticket_number,
  r['sr_return_quantity']::integer AS sr_return_quantity,
  r['sr_return_amt']::decimal(7,2) AS sr_return_amt,
  r['sr_return_tax']::decimal(7,2) AS sr_return_tax,
  r['sr_return_amt_inc_tax']::decimal(7,2) AS sr_return_amt_inc_tax,
  r['sr_fee']::decimal(7,2) AS sr_fee,
  r['sr_return_ship_cost']::decimal(7,2) AS sr_return_ship_cost,
  r['sr_refunded_cash']::decimal(7,2) AS sr_refunded_cash,
  r['sr_reversed_charge']::decimal(7,2) AS sr_reversed_charge,
  r['sr_store_credit']::decimal(7,2) AS sr_store_credit,
  r['sr_net_loss']::decimal(7,2) AS sr_net_loss FROM
read_parquet('/home/test/tpcds_tables100/store_returns.parquet', binary_as_string => true) r;

CREATE VIEW household_demographics_parquet AS SELECT
  r['hd_demo_sk']::integer AS hd_demo_sk,
  r['hd_income_band_sk']::integer AS hd_income_band_sk,
  r['hd_buy_potential']::text AS hd_buy_potential,
  r['hd_dep_count']::integer AS hd_dep_count,
  r['hd_vehicle_count']::integer AS hd_vehicle_count FROM
read_parquet('/home/test/tpcds_tables100/household_demographics.parquet', binary_as_string => true) r;

CREATE VIEW web_page_parquet AS SELECT
  r['wp_web_page_sk']::integer AS wp_web_page_sk,
  r['wp_web_page_id']::text AS wp_web_page_id,
  r['wp_rec_start_date']::date AS wp_rec_start_date,
  r['wp_rec_end_date']::date AS wp_rec_end_date,
  r['wp_creation_date_sk']::integer AS wp_creation_date_sk,
  r['wp_access_date_sk']::integer AS wp_access_date_sk,
  r['wp_autogen_flag']::text AS wp_autogen_flag,
  r['wp_customer_sk']::integer AS wp_customer_sk,
  r['wp_url']::text AS wp_url,
  r['wp_type']::text AS wp_type,
  r['wp_char_count']::integer AS wp_char_count,
  r['wp_link_count']::integer AS wp_link_count,
  r['wp_image_count']::integer AS wp_image_count,
  r['wp_max_ad_count']::integer AS wp_max_ad_count FROM
read_parquet('/home/test/tpcds_tables100/web_page.parquet', binary_as_string => true) r;

CREATE VIEW promotion_parquet AS SELECT
  r['p_promo_sk']::integer AS p_promo_sk,
  r['p_promo_id']::text AS p_promo_id,
  r['p_start_date_sk']::integer AS p_start_date_sk,
  r['p_end_date_sk']::integer AS p_end_date_sk,
  r['p_item_sk']::integer AS p_item_sk,
  r['p_cost']::decimal(15,2) AS p_cost,
  r['p_response_target']::integer as p_response_target,
  r['p_promo_name']::text AS p_promo_name,
  r['p_channel_dmail']::text AS p_channel_dmail,
  r['p_channel_email']::text AS p_channel_email,
  r['p_channel_catalog']::text AS p_channel_catalog,
  r['p_channel_tv']::text AS p_channel_tv,
  r['p_channel_radio']::text AS p_channel_radio,
  r['p_channel_press']::text AS p_channel_press,
  r['p_channel_event']::text AS p_channel_event,
  r['p_channel_demo']::text AS p_channel_demo,
  r['p_channel_details']::text AS p_channel_details,
  r['p_purpose']::text AS p_purpose,
  r['p_discount_active']::text AS p_discount_active FROM
read_parquet('/home/test/tpcds_tables100/promotion.parquet', binary_as_string => true) r;

CREATE VIEW catalog_page_parquet AS SELECT
  r['cp_catalog_page_sk']::integer AS cp_catalog_page_sk,
  r['cp_catalog_page_id']::text AS cp_catalog_page_id,
  r['cp_start_date_sk']::integer AS cp_start_date_sk,
  r['cp_end_date_sk']::integer AS cp_end_date_sk,
  r['cp_department']::text AS cp_department,
  r['cp_catalog_number']::integer AS cp_catalog_number,
  r['cp_catalog_page_number']::integer AS cp_catalog_page_number,
  r['cp_description']::text AS cp_description,
  r['cp_type']::text AS cp_type FROM
read_parquet('/home/test/tpcds_tables100/catalog_page.parquet', binary_as_string => true) r;

CREATE VIEW inventory_parquet AS SELECT
  r['inv_date_sk']::integer AS inv_date_sk,
  r['inv_item_sk']::integer AS inv_item_sk,
  r['inv_warehouse_sk']::integer AS inv_warehouse_sk,
  r['inv_quantity_on_hand']::integer AS inv_quantity_on_hand FROM
read_parquet('/home/test/tpcds_tables100/inventory.parquet', binary_as_string => true) r;

CREATE VIEW catalog_returns_parquet AS SELECT
  r['cr_returned_date_sk']::integer AS cr_returned_date_sk,
  r['cr_returned_time_sk']::integer AS cr_returned_time_sk,
  r['cr_item_sk']::integer AS cr_item_sk,
  r['cr_refunded_customer_sk']::integer AS cr_refunded_customer_sk,
  r['cr_refunded_cdemo_sk']::integer AS cr_refunded_cdemo_sk,
  r['cr_refunded_hdemo_sk']::integer AS cr_refunded_hdemo_sk,
  r['cr_refunded_addr_sk']::integer AS cr_refunded_addr_sk,
  r['cr_returning_customer_sk']::integer AS cr_returning_customer_sk,
  r['cr_returning_cdemo_sk']::integer AS cr_returning_cdemo_sk,
  r['cr_returning_hdemo_sk']::integer AS cr_returning_hdemo_sk,
  r['cr_returning_addr_sk']::integer AS cr_returning_addr_sk,
  r['cr_call_center_sk']::integer AS cr_call_center_sk,
  r['cr_catalog_page_sk']::integer AS cr_catalog_page_sk,
  r['cr_ship_mode_sk']::integer AS cr_ship_mode_sk,
  r['cr_warehouse_sk']::integer AS cr_warehouse_sk,
  r['cr_reason_sk']::integer AS cr_reason_sk,
  r['cr_order_number']::integer AS cr_order_number,
  r['cr_return_quantity']::integer AS cr_return_quantity,
  r['cr_return_amount']::decimal(7,2) AS cr_return_amount,
  r['cr_return_tax']::decimal(7,2) AS cr_return_tax,
  r['cr_return_amt_inc_tax']::decimal(7,2) AS cr_return_amt_inc_tax,
  r['cr_fee']::decimal(7,2) AS cr_fee,
  r['cr_return_ship_cost']::decimal(7,2) AS cr_return_ship_cost,
  r['cr_refunded_cash']::decimal(7,2) AS cr_refunded_cash,
  r['cr_reversed_charge']::decimal(7,2) AS cr_reversed_charge,
  r['cr_store_credit']::decimal(7,2) AS cr_store_credit,
  r['cr_net_loss']::decimal(7,2) AS cr_net_loss FROM
read_parquet('/home/test/tpcds_tables100/catalog_returns.parquet', binary_as_string => true) r;

CREATE VIEW web_returns_parquet AS SELECT
  r['wr_returned_date_sk']::integer AS wr_returned_date_sk,
  r['wr_returned_time_sk']::integer AS wr_returned_time_sk,
  r['wr_item_sk']::integer AS wr_item_sk,
  r['wr_refunded_customer_sk']::integer AS wr_refunded_customer_sk,
  r['wr_refunded_cdemo_sk']::integer AS wr_refunded_cdemo_sk,
  r['wr_refunded_hdemo_sk']::integer AS wr_refunded_hdemo_sk,
  r['wr_refunded_addr_sk']::integer AS wr_refunded_addr_sk,
  r['wr_returning_customer_sk']::integer AS wr_returning_customer_sk,
  r['wr_returning_cdemo_sk']::integer AS wr_returning_cdemo_sk,
  r['wr_returning_hdemo_sk']::integer AS wr_returning_hdemo_sk,
  r['wr_returning_addr_sk']::integer AS wr_returning_addr_sk,
  r['wr_web_page_sk']::integer AS wr_web_page_sk,
  r['wr_reason_sk']::integer AS wr_reason_sk,
  r['wr_order_number']::integer AS wr_order_number,
  r['wr_return_quantity']::integer AS wr_return_quantity,
  r['wr_return_amt']::decimal(7,2) AS wr_return_amt,
  r['wr_return_tax']::decimal(7,2) AS wr_return_tax,
  r['wr_return_amt_inc_tax']::decimal(7,2) AS wr_return_amt_inc_tax,
  r['wr_fee']::decimal(7,2) AS wr_fee,
  r['wr_return_ship_cost']::decimal(7,2) AS wr_return_ship_cost,
  r['wr_refunded_cash']::decimal(7,2) AS wr_refunded_cash,
  r['wr_reversed_charge']::decimal(7,2) AS wr_reversed_charge,
  r['wr_account_credit']::decimal(7,2) AS wr_account_credit,
  r['wr_net_loss']::decimal(7,2) AS wr_net_loss FROM
read_parquet('/home/test/tpcds_tables100/web_returns.parquet', binary_as_string => true) r;

CREATE VIEW web_sales_parquet AS SELECT
  r['ws_sold_date_sk']::integer AS ws_sold_date_sk,
  r['ws_sold_time_sk']::integer AS ws_sold_time_sk,
  r['ws_ship_date_sk']::integer AS ws_ship_date_sk,
  r['ws_item_sk']::integer AS ws_item_sk,
  r['ws_bill_customer_sk']::integer AS ws_bill_customer_sk,
  r['ws_bill_cdemo_sk']::integer AS ws_bill_cdemo_sk,
  r['ws_bill_hdemo_sk']::integer AS ws_bill_hdemo_sk,
  r['ws_bill_addr_sk']::integer AS ws_bill_addr_sk,
  r['ws_ship_customer_sk']::integer AS ws_ship_customer_sk,
  r['ws_ship_cdemo_sk']::integer AS ws_ship_cdemo_sk,
  r['ws_ship_hdemo_sk']::integer AS ws_ship_hdemo_sk,
  r['ws_ship_addr_sk']::integer AS ws_ship_addr_sk,
  r['ws_web_page_sk']::integer AS ws_web_page_sk,
  r['ws_web_site_sk']::integer AS ws_web_site_sk,
  r['ws_ship_mode_sk']::integer AS ws_ship_mode_sk,
  r['ws_warehouse_sk']::integer AS ws_warehouse_sk,
  r['ws_promo_sk']::integer AS ws_promo_sk,
  r['ws_order_number']::integer AS ws_order_number,
  r['ws_quantity']::integer AS ws_quantity,
  r['ws_wholesale_cost']::decimal(7,2) AS ws_wholesale_cost,
  r['ws_list_price']::decimal(7,2) AS ws_list_price,
  r['ws_sales_price']::decimal(7,2) AS ws_sales_price,
  r['ws_ext_discount_amt']::decimal(7,2) AS ws_ext_discount_amt,
  r['ws_ext_sales_price']::decimal(7,2) AS ws_ext_sales_price,
  r['ws_ext_wholesale_cost']::decimal(7,2) AS ws_ext_wholesale_cost,
  r['ws_ext_list_price']::decimal(7,2) AS ws_ext_list_price,
  r['ws_ext_tax']::decimal(7,2) AS ws_ext_tax,
  r['ws_coupon_amt']::decimal(7,2) AS ws_coupon_amt,
  r['ws_ext_ship_cost']::decimal(7,2) AS ws_ext_ship_cost,
  r['ws_net_paid']::decimal(7,2) AS ws_net_paid,
  r['ws_net_paid_inc_tax']::decimal(7,2) AS ws_net_paid_inc_tax,
  r['ws_net_paid_inc_ship']::decimal(7,2) AS ws_net_paid_inc_ship,
  r['ws_net_paid_inc_ship_tax']::decimal(7,2) AS ws_net_paid_inc_ship_tax,
  r['ws_net_profit']::decimal(7,2) AS ws_net_profit FROM
read_parquet('/home/test/tpcds_tables100/web_sales.parquet', binary_as_string => true) r;

CREATE VIEW catalog_sales_parquet AS SELECT
  r['cs_sold_date_sk']::integer AS cs_sold_date_sk,
  r['cs_sold_time_sk']::integer AS cs_sold_time_sk,
  r['cs_ship_date_sk']::integer AS cs_ship_date_sk,
  r['cs_bill_customer_sk']::integer AS cs_bill_customer_sk,
  r['cs_bill_cdemo_sk']::integer AS cs_bill_cdemo_sk,
  r['cs_bill_hdemo_sk']::integer AS cs_bill_hdemo_sk,
  r['cs_bill_addr_sk']::integer AS cs_bill_addr_sk,
  r['cs_ship_customer_sk']::integer AS cs_ship_customer_sk,
  r['cs_ship_cdemo_sk']::integer AS cs_ship_cdemo_sk,
  r['cs_ship_hdemo_sk']::integer AS cs_ship_hdemo_sk,
  r['cs_ship_addr_sk']::integer AS cs_ship_addr_sk,
  r['cs_call_center_sk']::integer AS cs_call_center_sk,
  r['cs_catalog_page_sk']::integer AS cs_catalog_page_sk,
  r['cs_ship_mode_sk']::integer AS cs_ship_mode_sk,
  r['cs_warehouse_sk']::integer AS cs_warehouse_sk,
  r['cs_item_sk']::integer AS cs_item_sk,
  r['cs_promo_sk']::integer AS cs_promo_sk,
  r['cs_order_number']::integer AS cs_order_number,
  r['cs_quantity']::integer AS cs_quantity,
  r['cs_wholesale_cost']::decimal(7,2) AS cs_wholesale_cost,
  r['cs_list_price']::decimal(7,2) AS cs_list_price,
  r['cs_sales_price']::decimal(7,2) AS cs_sales_price,
  r['cs_ext_discount_amt']::decimal(7,2) AS cs_ext_discount_amt,
  r['cs_ext_sales_price']::decimal(7,2) AS cs_ext_sales_price,
  r['cs_ext_wholesale_cost']::decimal(7,2) AS cs_ext_wholesale_cost,
  r['cs_ext_list_price']::decimal(7,2) AS cs_ext_list_price,
  r['cs_ext_tax']::decimal(7,2) AS cs_ext_tax,
  r['cs_coupon_amt']::decimal(7,2) AS cs_coupon_amt,
  r['cs_ext_ship_cost']::decimal(7,2) AS cs_ext_ship_cost,
  r['cs_net_paid']::decimal(7,2) AS cs_net_paid,
  r['cs_net_paid_inc_tax']::decimal(7,2) AS cs_net_paid_inc_tax,
  r['cs_net_paid_inc_ship']::decimal(7,2) AS cs_net_paid_inc_ship,
  r['cs_net_paid_inc_ship_tax']::decimal(7,2) AS cs_net_paid_inc_ship_tax,
  r['cs_net_profit']::decimal(7,2) AS cs_net_profit FROM
read_parquet('/home/test/tpcds_tables100/catalog_sales.parquet', binary_as_string => true) r;

CREATE VIEW store_sales_parquet AS SELECT
  r['ss_sold_date_sk']::integer AS ss_sold_date_sk,
  r['ss_sold_time_sk']::integer AS ss_sold_time_sk,
  r['ss_item_sk']::integer AS ss_item_sk,
  r['ss_customer_sk']::integer as ss_customer_sk,
  r['ss_cdemo_sk']::integer AS ss_cdemo_sk,
  r['ss_hdemo_sk']::integer AS ss_hdemo_sk,
  r['ss_addr_sk']::integer AS ss_addr_sk,
  r['ss_store_sk']::integer AS ss_store_sk,
  r['ss_promo_sk']::integer AS ss_promo_sk,
  r['ss_ticket_number']::integer AS ss_ticket_number,
  r['ss_quantity']::integer AS ss_quantity,
  r['ss_wholesale_cost']::decimal(7,2) AS ss_wholesale_cost,
  r['ss_list_price']::decimal(7,2) AS ss_list_price,
  r['ss_sales_price']::decimal(7,2) AS ss_sales_price,
  r['ss_ext_discount_amt']::decimal(7,2) AS ss_ext_discount_amt,
  r['ss_ext_sales_price']::decimal(7,2) AS ss_ext_sales_price,
  r['ss_ext_wholesale_cost']::decimal(7,2) AS ss_ext_wholesale_cost,
  r['ss_ext_list_price']::decimal(7,2) AS ss_ext_list_price,
  r['ss_ext_tax']::decimal(7,2) AS ss_ext_tax,
  r['ss_coupon_amt']::decimal(7,2) AS ss_coupon_amt,
  r['ss_net_paid']::decimal(7,2) AS ss_net_paid,
  r['ss_net_paid_inc_tax']::decimal(7,2) AS ss_net_paid_inc_tax,
  r['ss_net_profit']::decimal(7,2) AS ss_net_profit FROM
read_parquet('/home/test/tpcds_tables100/store_sales.parquet', binary_as_string => true) r;

C.2.2. Тест производительности TPC-DS: коэффициент масштабирования 100

Таблица C.7. Тестовые данные

Данные

Общий размер

Аналитические таблицы в формате CSV

100.3 ГБ

Аналитические таблицы в формате Parquet

24.4 ГБ

Таблица C.8. Информация о таблице

Аналитическая таблица

Количество строк

call_center

30

catalog_name

20 400

catalog_returns

14 404 374

catalog_sales

143 997 065

customer_address

1 000 000

customer_demographics

1 920 800

customer

2 000 000

date_dim

73 049

household_demographics

7200

income_band

20

inventory

399 330 000

item

204 000

promotion

1000

reason

55

ship_mode

20

store

402

store_returns

28 795 080

store_sales

287 997 024

time_dim

86 400

warehouse

15

web_page

2040

web_returns

7 197 670

web_sales

72 001 237

web_site

24

Таблица C.9. Результаты теста

Запрос

Продолжительность (сек)

user time (сек)

sys time (сек)

max RSS (МБ)

avg RSS (МБ)

read IO (МБ)

write IO (МБ)

Q1

1.17

3.22

0.93

1192.97

491.60

477.13

0.00

Q1 — второе выполнение

0.63

3.03

0.69

988.18

594.60

0.00

0.00

Q2

2.50

8.59

0.86

235.27

174.93

1288.71

0.00

Q2 — второе выполнение

1.43

8.75

0.20

36.79

18.57

0.00

0.00

Q3

3.37

6.17

1.58

217.61

156.81

2520.28

0.00

Q3 — второе выполнение

1.06

5.57

0.30

115.94

98.99

0.00

0.00

Q4

16.10

77.26

7.36

3724.82

2715.21

6804.64

0.00

Q4 — второе выполнение

12.44

75.30

4.32

3484.33

2438.37

0.00

0.00

Q5

6.53

14.17

3.97

262.91

86.14

4917.16

0.00

Q5 — второе выполнение

2.49

12.63

0.83

655.33

334.02

0.00

0.00

Q6

2.88

5.46

1.67

803.32

248.04

2049.75

0.00

Q6 — второе выполнение

1.12

5.22

0.54

670.04

256.43

0.00

0.00

Q7

5.08

14.50

3.06

273.12

219.21

4417.77

0.00

Q7 — второе выполнение

2.07

12.82

0.60

304.13

203.10

0.00

0.00

Q8

2.57

6.02

1.53

126.75

75.49

1832.75

0.00

Q8 — второе выполнение

1.07

5.23

0.33

172.51

125.57

0.00

0.00

Q9

11.98

45.17

4.10

1231.43

987.05

3097.12

0.00

Q9 — второе выполнение

9.59

44.64

1.95

1168.15

943.60

0.00

0.00

Q10

2.00

4.45

0.78

291.94

197.11

735.45

0.00

Q10 — второе выполнение

1.14

4.33

0.25

260.18

184.88

0.00

0.00

Q11

9.82

48.49

4.96

2943.27

2084.55

3595.08

0.00

Q11 — второе выполнение

7.52

46.24

2.91

2814.27

1953.61

0.00

0.00

Q12

1.12

1.24

0.71

114.03

46.17

782.98

0.00

Q12 — второе выполнение

0.33

1.06

0.15

81.71

65.32

0.00

0.00

Q13

7.19

25.40

4.26

337.76

170.22

6412.03

0.00

Q13 — второе выполнение

3.43

23.90

0.87

277.51

247.07

0.00

0.00

Q14

55.35

351.97

27.29

12440.79

6139.15

3974.71

0.00

Q14 — второе выполнение

52.91

350.84

25.29

12300.06

6103.95

0.00

0.00

Q15

0.60

0.44

0.22

273.75

98.49

59.92

0.00

Q15 — второе выполнение

0.47

0.99

0.21

278.39

155.70

0.00

0.00

Q16

0.82

0.68

0.21

138.67

37.96

133.39

0.00

Q16 — второе выполнение

0.55

0.36

0.07

130.62

88.34

0.00

0.00

Q17

3.77

10.79

2.32

1011.01

435.42

2540.09

0.00

Q17 — второе выполнение

1.97

9.47

0.94

938.59

603.75

0.00

0.00

Q18

2.72

7.33

2.07

849.32

520.70

1880.99

0.00

Q18 — второе выполнение

1.44

6.90

0.79

485.93

271.32

0.00

0.00

Q19

4.37

8.49

2.51

366.98

262.37

3507.33

0.00

Q19 — второе выполнение

1.40

7.46

0.45

305.40

241.06

0.00

0.00

Q20

0.51

0.48

0.14

168.94

67.48

57.51

0.00

Q20 — второе выполнение

0.34

0.41

0.09

103.89

63.66

0.00

0.00

Q21

0.38

0.49

0.15

94.69

28.35

71.18

0.00

Q21 — второе выполнение

0.22

0.31

0.09

48.75

48.75

0.00

0.00

Q22

5.12

35.25

2.51

3978.26

3339.97

288.99

0.00

Q22 — второе выполнение

4.84

34.30

2.40

3754.82

3294.72

0.00

0.00

Q23

34.78

211.18

29.75

14410.55

5756.93

3619.45

0.00

Q23 — второе выполнение

33.42

214.81

28.10

14177.65

7060.66

0.00

0.00

Q24

8.05

38.52

6.51

3716.41

2728.57

3818.02

0.00

Q24 — второе выполнение

5.98

37.81

4.04

3100.28

2448.52

0.00

0.00

Q25

1.91

9.35

0.90

593.10

330.63

0.13

0.00

Q25 — второе выполнение

1.89

9.57

0.98

752.11

451.65

0.00

0.00

Q26

0.95

1.95

0.46

50.44

17.93

464.31

0.00

Q26 — второе выполнение

0.52

1.48

0.17

88.94

60.80

0.00

0.00

Q27

5.23

36.54

1.91

257.18

208.34

0.00

0.00

Q27 — второе выполнение

5.22

36.46

2.00

255.22

223.97

0.00

0.00

Q28

9.51

49.79

3.08

569.21

488.75

2391.03

0.00

Q28 — второе выполнение

8.00

49.99

1.72

522.01

464.69

0.00

0.00

Q29

4.30

11.96

2.13

477.48

334.80

2970.54

0.00

Q29 — второе выполнение

2.04

10.66

0.60

397.14

304.42

0.00

0.00

Q30

0.60

1.38

0.53

650.11

298.65

260.07

0.00

Q30 — второе выполнение

0.34

1.06

0.36

523.42

360.41

0.00

0.00

Q31

3.60

10.84

2.15

344.71

245.75

2707.19

0.00

Q31 — второе выполнение

1.68

9.07

0.39

315.16

254.40

0.00

0.00

Q32

0.64

0.57

0.15

160.23

72.60

73.26

0.00

Q32 — второе выполнение

0.50

0.38

0.08

105.56

76.21

0.00

0.00

Q33

4.53

8.28

3.01

341.25

182.34

3831.89

0.00

Q33 — второе выполнение

1.59

7.33

0.51

301.92

226.27

0.13

0.00

Q34

2.57

9.14

1.66

267.28

189.25

1306.16

0.00

Q34 — второе выполнение

1.56

8.59

0.67

189.05

139.17

0.00

0.00

Q35

2.84

10.58

1.75

1329.99

524.17

798.14

0.00

Q35 — второе выполнение

2.03

9.89

1.02

1279.56

508.52

0.00

0.00

Q36

9.65

39.32

6.48

230.54

98.88

9484.43

0.00

Q36 — второе выполнение

5.25

36.71

1.64

287.10

243.82

0.00

0.00

Q37

1.09

1.16

0.50

230.54

140.18

597.99

0.00

Q37 — второе выполнение

0.48

0.98

0.15

72.25

23.13

0.00

0.00

Q38

3.25

14.29

1.49

944.50

622.75

796.22

0.00

Q38 — второе выполнение

2.45

13.89

0.89

931.42

562.25

0.00

0.00

Q39

1.25

4.34

0.70

834.70

528.92

69.11

0.00

Q39 — второе выполнение

1.09

4.19

0.68

630.05

481.47

0.00

0.00

Q40

0.79

1.61

0.56

753.05

322.24

252.40

0.00

Q40 — второе выполнение

0.53

1.29

0.43

642.39

306.34

0.00

0.00

Q41

0.05

0.00

0.00

0.00

0.00

0.00

0.00

Q41 — второе выполнение

0.05

0.00

0.00

0.00

0.00

0.00

0.00

Q42

3.38

5.36

1.83

173.89

59.68

2911.17

0.00

Q42 — второе выполнение

0.93

4.48

0.31

146.17

112.09

0.00

0.00

Q43

2.58

9.63

1.30

173.25

136.36

1634.61

0.00

Q43 — второе выполнение

1.47

9.06

0.24

118.13

94.52

0.00

0.00

Q44

5.46

16.37

3.45

376.72

266.74

2359.85

0.00

Q44 — второе выполнение

3.21

15.29

1.26

413.08

320.93

0.00

0.00

Q45

1.24

2.39

0.78

347.48

245.26

863.27

0.00

Q45 — второе выполнение

0.48

1.93

0.22

286.66

225.99

0.00

0.00

Q46

4.24

14.46

2.41

149.67

40.76

2965.36

0.00

Q46 — второе выполнение

2.11

13.71

0.56

368.88

190.46

0.00

0.00

Q47

4.85

25.55

2.31

947.38

691.41

2619.97

0.00

Q47 — второе выполнение

3.54

23.57

1.10

915.69

641.71

0.00

0.00

Q48

5.03

17.65

2.88

459.10

392.36

3708.38

0.00

Q48 — второе выполнение

2.52

17.06

0.60

362.76

323.18

0.00

0.00

Q49

6.56

12.66

3.76

407.96

309.14

5597.77

0.00

Q49 — второе выполнение

2.18

11.22

0.69

332.85

238.94

0.00

0.00

Q50

3.51

12.97

1.57

260.37

153.21

2347.37

0.00

Q50 — второе выполнение

1.88

11.82

0.39

193.53

156.92

0.00

0.00

Q51

9.38

51.42

5.17

3372.08

2109.36

3313.81

0.00

Q51 — второе выполнение

7.44

50.63

3.97

3197.07

2224.74

0.00

0.00

Q52

0.96

4.29

0.35

118.52

86.21

0.00

0.00

Q52 — второе выполнение

0.97

4.36

0.33

98.36

75.20

0.00

0.00

Q53

3.83

27.57

0.45

31.68

21.46

0.00

0.00

Q53 — второе выполнение

3.75

26.24

0.46

127.29

113.64

0.00

0.00

Q54

10.95

37.76

7.35

514.70

409.99

10135.38

0.00

Q54 — второе выполнение

5.15

34.27

1.74

400.75

319.33

0.00

0.00

Q55

3.49

5.16

1.95

216.37

173.05

2949.66

0.00

Q55 — второе выполнение

0.92

4.53

0.30

145.31

109.57

0.00

0.00

Q56

4.89

8.23

3.04

295.09

221.59

3765.99

0.00

Q56 — второе выполнение

1.62

7.17

0.54

323.50

234.02

0.13

0.00

Q57

1.35

5.90

0.55

450.29

242.16

338.17

0.00

Q57 — второе выполнение

1.10

5.50

0.32

318.67

203.66

0.00

0.00

Q58

4.68

6.37

2.57

290.60

193.40

3816.84

0.00

Q58 — второе выполнение

1.35

5.40

0.45

322.89

209.36

0.00

0.00

Q59

4.26

22.92

2.79

3261.81

1652.81

1638.08

0.00

Q59 — второе выполнение

3.24

21.50

1.92

3080.73

1556.97

0.00

0.00

Q60

4.76

8.76

2.73

409.08

322.64

3774.54

0.00

Q60 — второе выполнение

1.65

7.47

0.50

377.33

265.09

0.00

0.00

Q61

0.82

0.57

0.15

238.39

116.60

57.09

0.00

Q61 — второе выполнение

0.66

0.49

0.07

165.11

116.57

0.00

0.00

Q62

1.05

3.43

0.49

125.36

75.53

500.09

0.00

Q62 — второе выполнение

0.55

2.46

0.11

66.26

55.99

0.00

0.00

Q63

4.66

26.13

1.69

262.25

214.19

2620.00

0.00

Q63 — второе выполнение

3.67

26.30

0.41

111.27

88.72

0.00

0.00

Q64

8.48

33.17

4.94

1535.12

872.82

6340.82

0.00

Q64 — второе выполнение

4.82

30.85

1.84

1511.87

929.80

0.00

0.00

Q65

5.47

24.22

3.77

2605.05

1520.66

2627.54

0.00

Q65 — второе выполнение

3.85

23.73

2.22

2227.14

1299.75

0.00

0.00

Q66

0.54

0.31

0.09

80.12

34.42

32.08

0.00

Q66 — второе выполнение

0.41

0.25

0.04

64.41

56.79

0.00

0.00

Q67

32.80

184.39

52.09

27685.02

15027.58

9539.32

0.00

Q67 — второе выполнение

31.85

187.17

53.17

25786.45

14302.98

0.00

0.00

Q68

2.26

14.54

0.80

243.20

172.12

0.00

0.00

Q68 — второе выполнение

2.26

14.50

0.84

171.16

146.57

0.00

0.00

Q69

2.06

4.91

0.95

331.04

221.16

781.57

0.00

Q69 — второе выполнение

1.23

4.18

0.23

250.59

169.31

0.00

0.00

Q70

10.69

48.99

7.30

251.75

155.97

9527.83

0.00

Q70 — второе выполнение

6.62

45.26

2.33

274.30

223.94

0.00

0.00

Q71

5.41

8.94

3.24

293.62

155.80

4213.18

0.00

Q71 — второе выполнение

1.83

8.05

0.54

244.07

181.46

0.00

0.00

Q72

3.35

14.40

2.39

1738.82

899.55

1833.04

0.00

Q72 — второе выполнение

2.31

13.71

1.40

1675.54

946.35

0.00

0.00

Q73

2.56

9.00

1.64

165.74

73.43

1305.82

0.00

Q73 — второе выполнение

1.52

8.59

0.80

200.02

148.51

0.00

0.00

Q74

7.45

32.32

3.67

1555.93

992.62

2716.92

0.00

Q74 — второе выполнение

5.62

31.92

2.05

1637.58

1022.67

0.00

0.00

Q75

7.99

23.62

5.38

2366.00

895.90

5997.67

0.00

Q75 — второе выполнение

3.82

22.79

2.27

2518.67

1103.22

0.00

0.00

Q76

6.63

12.67

4.58

243.60

2.41

6114.49

0.00

Q76 — второе выполнение

2.05

10.82

0.56

260.00

209.38

0.00

0.00

Q77

5.64

10.18

3.86

305.27

229.59

4930.04

0.00

Q77 — второе выполнение

1.87

9.14

0.56

278.83

206.50

0.00

0.00

Q78

10.97

48.95

10.27

8747.98

4708.29

6069.71

0.00

Q78 — второе выполнение

7.94

47.75

7.26

8704.53

4716.97

0.00

0.00

Q79

4.65

18.15

3.10

160.60

26.10

2955.89

0.00

Q79 — второе выполнение

2.63

17.10

1.23

535.23

307.60

0.00

0.00

Q80

8.35

18.92

6.13

2152.33

1599.29

6825.18

0.00

Q80 — второе выполнение

3.17

16.97

2.00

1986.95

1368.29

0.00

0.00

Q81

0.54

1.21

0.56

704.92

336.28

219.25

0.00

Q81 — второе выполнение

0.34

1.30

0.48

603.72

463.50

0.00

0.00

Q82

1.91

2.89

1.05

222.29

136.18

1365.56

0.00

Q82 — второе выполнение

0.74

2.59

0.26

127.56

92.90

0.00

0.00

Q83

0.64

0.86

0.40

126.32

76.58

387.46

0.00

Q83 — второе выполнение

0.21

0.47

0.09

70.56

70.56

0.00

0.00

Q84

0.41

0.46

0.24

147.61

65.52

174.04

0.00

Q84 — второе выполнение

0.15

0.00

0.00

0.00

0.00

0.00

0.00

Q85

2.51

6.82

2.13

1582.14

878.51

1627.35

0.00

Q85 — второе выполнение

1.19

6.44

1.06

1448.66

987.85

0.00

0.00

Q86

3.80

11.81

2.62

212.07

71.60

3520.44

0.00

Q86 — второе выполнение

1.66

10.28

0.79

193.19

163.83

0.00

0.00

Q87

3.37

15.27

1.45

917.36

558.52

794.04

0.00

Q87 — второе выполнение

2.63

14.85

0.89

922.12

605.15

0.00

0.00

Q88

7.75

37.90

1.71

747.62

607.90

1138.95

0.00

Q88 — второе выполнение

6.83

37.41

0.94

635.72

525.87

0.00

0.00

Q89

4.41

23.04

1.78

228.59

173.88

2618.04

0.00

Q89 — второе выполнение

3.35

23.85

0.46

178.50

152.58

0.00

0.00

Q90

0.85

1.24

0.34

212.45

123.93

373.87

0.00

Q90 — второе выполнение

0.44

1.75

0.14

99.01

71.43

0.00

0.00

Q91

0.28

0.07

0.05

41.54

23.18

45.54

0.00

Q91 — второе выполнение

0.10

0.00

0.00

0.00

0.00

0.00

0.00

Q92

1.31

2.22

0.81

200.43

140.33

830.58

0.00

Q92 — второе выполнение

0.50

1.65

0.17

75.73

46.71

0.00

0.00

Q93

1.04

2.55

1.06

1415.03

541.22

331.42

0.00

Q93 — второе выполнение

0.77

2.40

0.87

1334.89

672.78

0.00

0.00

Q94

0.55

1.63

0.21

106.06

75.45

0.00

0.00

Q94 — второе выполнение

0.57

2.47

0.21

140.13

100.85

0.00

0.00

Q95

7.49

40.62

6.17

3596.52

2449.48

1140.46

0.00

Q95 — второе выполнение

6.41

40.35

4.71

3644.67

2890.47

0.00

0.00

Q96

1.68

4.97

0.84

215.09

163.58

1090.45

0.00

Q96 — второе выполнение

0.88

4.66

0.17

112.72

95.50

0.00

0.00

Q97

3.97

16.53

2.97

2424.42

1272.05

2167.17

0.00

Q97 — второе выполнение

2.77

15.64

1.97

2599.21

1429.85

0.00

0.00

Q98

3.56

5.89

2.13

172.88

123.42

3016.77

0.00

Q98 — второе выполнение

1.28

5.35

0.43

309.03

205.25

0.00

0.00

Q99

0.97

3.81

0.19

115.52

77.30

169.06

0.00

Q99 — второе выполнение

0.75

3.98

0.09

70.03

58.50

0.00

0.00

C.3. Тест производительности ClickBench

C.3.1. Создание аналитических таблиц для теста производительности ClickBench

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

COPY hits
TO '/home/test/clickbench/hits.parquet'
(FORMAT 'parquet', COMPRESSION 'zstd');

CREATE VIEW hits_parquet AS SELECT
  r['watchid']::bigint AS watchid,
  r['javaenable']::smallint AS javaenable,
  r['title']::text AS title,
  r['goodevent']::smallint AS goodevent,
  r['eventtime']::timestamp AS eventtime,
  r['eventdate']::date AS eventdate,
  r['counterid']::integer AS counterid,
  r['clientip']::integer AS clientip,
  r['regionid']::integer AS regionid,
  r['userid']::bigint AS userid,
  r['counterclass']::smallint AS counterclass,
  r['os']::smallint AS os,
  r['useragent']::smallint AS useragent,
  r['url']::text AS url,
  r['referer']::text AS referer,
  r['isrefresh']::smallint AS isrefresh,
  r['referercategoryid']::smallint AS referercategoryid,
  r['refererregionid']::integer AS refererregionid,
  r['urlcategoryid']::smallint AS urlcategoryid,
  r['urlregionid']::integer AS urlregionid,
  r['resolutionwidth']::smallint AS resolutionwidth,
  r['resolutionheight']::smallint AS resolutionheight,
  r['resolutiondepth']::smallint AS resolutiondepth,
  r['flashmajor']::smallint AS flashmajor,
  r['flashminor']::smallint AS flashminor,
  r['flashminor2']::text AS flashminor2,
  r['netmajor']::smallint AS netmajor,
  r['netminor']::smallint AS netminor,
  r['useragentmajor']::smallint AS useragentmajor,
  r['useragentminor']::text AS useragentminor,
  r['cookieenable']::smallint AS cookieenable,
  r['javascriptenable']::smallint AS javascriptenable,
  r['ismobile']::smallint AS ismobile,
  r['mobilephone']::smallint AS mobilephone,
  r['mobilephonemodel']::text AS mobilephonemodel,
  r['params']::text AS params,
  r['ipnetworkid']::integer AS ipnetworkid,
  r['traficsourceid']::smallint AS traficsourceid,
  r['searchengineid']::smallint AS searchengineid,
  r['searchphrase']::text AS searchphrase,
  r['advengineid']::smallint AS advengineid,
  r['isartifical']::smallint AS isartifical,
  r['windowclientwidth']::smallint AS windowclientwidth,
  r['windowclientheight']::smallint AS windowclientheight,
  r['clienttimezone']::smallint AS clienttimezone,
  r['clienteventtime']::timestamp AS clienteventtime,
  r['silverlightversion1']::smallint AS silverlightversion1,
  r['silverlightversion2']::smallint AS silverlightversion2,
  r['silverlightversion3']::integer AS silverlightversion3,
  r['silverlightversion4']::smallint AS silverlightversion4,
  r['pagecharset']::text AS pagecharset,
  r['codeversion']::integer AS codeversion,
  r['islink']::smallint AS islink,
  r['isdownload']::smallint AS isdownload,
  r['isnotbounce']::smallint AS isnotbounce,
  r['funiqid']::bigint AS funiqid,
  r['originalurl']::text AS originalurl,
  r['hid']::integer AS hid,
  r['isoldcounter']::smallint AS isoldcounter,
  r['isevent']::smallint AS isevent,
  r['isparameter']::smallint AS isparameter,
  r['dontcounthits']::smallint AS dontcounthits,
  r['withhash']::smallint AS withhash,
  r['hitcolor']::text AS hitcolor,
  r['localeventtime']::timestamp AS localeventtime,
  r['age']::smallint AS age,
  r['sex']::smallint AS sex,
  r['income']::smallint AS income,
  r['interests']::smallint AS interests,
  r['robotness']::smallint AS robotness,
  r['remoteip']::integer AS remoteip,
  r['windowname']::integer AS windowname,
  r['openername']::integer AS openername,
  r['historylength']::smallint AS historylength,
  r['browserlanguage']::text AS browserlanguage,
  r['browsercountry']::text AS browsercountry,
  r['socialnetwork']::text AS socialnetwork,
  r['socialaction']::text AS socialaction,
  r['httperror']::smallint AS httperror,
  r['sendtiming']::integer AS sendtiming,
  r['dnstiming']::integer AS dnstiming,
  r['connecttiming']::integer AS connecttiming,
  r['responsestarttiming']::integer AS responsestarttiming,
  r['responseendtiming']::integer AS responseendtiming,
  r['fetchtiming']::integer AS fetchtiming,
  r['socialsourcenetworkid']::smallint AS socialsourcenetworkid,
  r['socialsourcepage']::text AS socialsourcepage,
  r['paramprice']::bigint AS paramprice,
  r['paramorderid']::text AS paramorderid,
  r['paramcurrency']::text AS paramcurrency,
  r['paramcurrencyid']::smallint AS paramcurrencyid,
  r['openstatservicename']::text AS openstatservicename,
  r['openstatcampaignid']::text AS openstatcampaignid,
  r['openstatadid']::text AS openstatadid,
  r['openstatsourceid']::text AS openstatsourceid,
  r['utmsource']::text AS utmsource,
  r['utmmedium']::text AS utmmedium,
  r['utmcampaign']::text AS utmcampaign,
  r['utmcontent']::text AS utmcontent,
  r['utmterm']::text AS utmterm,
  r['fromtag']::text AS fromtag,
  r['hasgclid']::smallint AS hasgclid,
  r['refererhash']::bigint AS refererhash,
  r['urlhash']::bigint AS urlhash,
  r['clid']::integer AS clid from
read_parquet('/home/test/clickbench/hits.parquet', binary_as_string => true) r;

C.3.2. Тест производительности ClickBench: коэффициент масштабирования 100

Таблица C.10. Тестовые данные

Данные

Общий размер

Аналитические таблицы в формате CSV

71.3 ГБ

Аналитические таблицы в формате Parquet

9.3 ГБ

База данных

66 ГБ

Таблица C.11. Информация о таблице

Аналитическая таблица

Количество строк

hits

99 997 497

Таблица C.12. Результаты теста

Запрос

Продолжительность (сек)

user time (сек)

sys time (сек)

max RSS (МБ)

avg RSS (МБ)

read IO (МБ)

write IO (МБ)

Q1

0.63

0.45

0.07

125.90

77.85

31.49

0.00

Q1 — второе выполнение

0.47

0.39

0.04

73.11

71.41

0.00

0.00

Q2

0.79

0.56

0.11

158.62

89.55

36.81

0.00

Q2 — второе выполнение

0.53

0.36

0.07

135.46

92.99

0.00

0.00

Q3

0.91

1.23

0.20

167.48

85.73

131.00

0.00

Q3 — второе выполнение

0.60

0.98

0.10

148.55

110.61

0.00

0.00

Q4

1.00

0.81

0.27

220.86

112.01

284.62

0.00

Q4 — второе выполнение

0.59

0.92

0.13

136.31

101.65

0.00

0.00

Q5

1.20

3.14

0.70

708.39

216.61

382.22

0.00

Q5 — второе выполнение

0.92

2.86

0.48

620.24

209.80

0.00

0.00

Q6

1.50

4.54

0.93

1163.14

486.90

479.83

0.00

Q6 — второе выполнение

1.16

4.55

0.75

954.81

361.70

0.00

0.00

Q7

0.77

0.77

0.14

144.44

88.64

48.86

0.00

Q7 — второе выполнение

0.57

0.35

0.08

137.39

90.48

0.00

0.00

Q8

0.70

0.43

0.08

155.98

84.44

33.83

0.00

Q8 — второе выполнение

0.57

0.66

0.08

143.61

102.05

0.00

0.00

Q9

1.43

3.44

0.74

747.97

321.12

489.98

0.00

Q9 — второе выполнение

1.02

3.87

0.49

605.55

290.13

0.00

0.00

Q10

1.68

4.81

0.91

707.90

302.82

587.95

0.00

Q10 — второе выполнение

1.20

4.67

0.54

712.61

317.29

0.00

0.00

Q11

1.11

1.32

0.43

124.87

33.22

342.63

0.00

Q11 — второе выполнение

0.68

0.82

0.13

163.49

98.62

0.00

0.00

Q12

1.14

1.55

0.46

317.14

144.63

348.34

0.00

Q12 — второе выполнение

0.69

0.88

0.15

92.59

22.58

0.00

0.00

Q13

1.46

4.85

1.03

1260.84

523.72

482.57

0.00

Q13 — второе выполнение

1.18

4.69

0.78

1129.12

486.61

0.00

0.00

Q14

2.04

7.44

1.51

1312.48

615.04

828.38

0.00

Q14 — второе выполнение

1.52

6.58

1.04

1338.21

582.66

0.00

0.00

Q15

1.62

5.31

1.05

1262.77

530.42

494.15

0.00

Q15 — второе выполнение

1.21

4.76

0.79

1167.94

527.60

0.00

0.00

Q16

1.26

2.82

0.78

745.76

198.17

384.04

0.00

Q16 — второе выполнение

0.95

2.69

0.54

770.43

240.03

0.00

0.00

Q17

2.14

7.74

1.90

2271.80

988.01

829.14

0.00

Q17 — второе выполнение

1.70

8.43

1.36

2292.72

1122.14

0.00

0.00

Q18

2.13

7.77

1.79

2191.19

940.75

831.77

0.00

Q18 — второе выполнение

1.71

8.45

1.44

2184.30

1042.97

0.00

0.00

Q19

3.17

14.12

3.17

3353.14

1553.31

1349.53

0.00

Q19 — второе выполнение

2.54

13.22

2.43

3812.80

1997.06

0.00

0.00

Q20

0.88

0.92

0.34

132.50

71.91

350.26

0.00

Q20 — второе выполнение

0.58

0.86

0.11

78.63

41.52

0.00

0.00

Q21

5.71

35.14

1.80

528.65

295.37

2665.79

0.00

Q21 — второе выполнение

4.96

34.58

0.84

765.54

507.69

0.00

0.00

Q22

4.12

17.66

2.49

1280.60

787.99

3064.32

0.00

Q22 — второе выполнение

2.77

16.86

1.14

1043.66

667.71

0.00

0.00

Q23

5.65

25.08

3.60

1052.83

760.07

4476.53

0.00

Q23 — второе выполнение

3.73

23.60

1.80

806.15

525.07

61.22

0.00

Q24

2.27

10.19

0.70

353.58

224.77

1040.53

0.00

Q24 — второе выполнение

1.90

10.03

0.32

253.96

177.31

0.00

0.00

Q25

0.79

0.88

0.23

200.04

83.95

151.16

0.00

Q25 — второе выполнение

0.65

0.63

0.12

176.91

100.47

0.43

0.00

Q26

1.17

2.49

0.38

220.63

108.22

388.89

0.00

Q26 — второе выполнение

0.86

2.96

0.17

220.31

128.92

0.00

0.00

Q27

0.89

0.89

0.22

174.16

53.80

164.66

0.00

Q27 — второе выполнение

0.65

0.93

0.11

140.21

91.47

0.00

0.00

Q28

3.66

13.22

2.22

1306.91

794.35

2635.39

0.00

Q28 — второе выполнение

2.24

12.38

1.06

1003.94

573.61

0.00

0.00

Q29

12.23

82.03

3.60

1164.64

638.60

2695.30

0.00

Q29 — второе выполнение

11.47

81.52

2.62

1625.54

1002.23

0.00

0.00

Q30

0.78

0.83

0.20

149.80

88.79

116.46

0.00

Q30 — второе выполнение

0.58

0.37

0.06

51.00

18.07

0.00

0.00

Q31

2.03

5.78

1.22

701.85

369.72

1021.30

0.00

Q31 — второе выполнение

1.26

4.69

0.53

603.29

274.75

0.00

0.00

Q32

3.33

5.87

2.15

757.51

366.02

2516.53

0.00

Q32 — второе выполнение

1.25

4.67

0.79

895.50

376.31

0.00

0.00

Q33

4.08

12.12

4.79

5902.10

3058.48

2069.20

0.00

Q33 — второе выполнение

2.58

12.02

4.27

5291.00

2890.68

0.00

0.00

Q34

4.17

17.30

5.64

5521.45

2674.25

2648.54

0.00

Q34 — второе выполнение

3.19

16.57

4.18

5923.27

2989.65

0.00

0.00

Q35

4.36

17.58

6.10

5529.96

2811.14

2648.79

0.00

Q35 — второе выполнение

3.30

16.83

4.73

5503.18

2698.39

0.00

0.00

Q36

1.15

3.17

0.79

352.95

99.13

221.50

0.00

Q36 — второе выполнение

1.07

3.51

0.67

838.83

285.11

0.00

0.00

Q37

0.90

0.98

0.52

353.64

116.84

208.02

0.00

Q37 — второе выполнение

0.64

0.65

0.26

186.30

63.08

0.00

0.00

Q38

0.89

0.91

0.41

118.58

6.67

154.77

0.00

Q38 — второе выполнение

0.61

0.71

0.23

119.66

2.78

0.00

0.00

Q39

0.87

0.65

0.25

265.94

42.13

103.84

0.00

Q39 — второе выполнение

0.62

0.66

0.23

245.41

88.15

0.02

0.00

Q40

1.01

1.04

0.56

394.09

110.50

210.13

0.00

Q40 — второе выполнение

0.74

1.33

0.61

793.19

283.12

0.00

0.00

Q41

0.76

0.59

0.21

203.92

89.32

107.98

0.00

Q41 — второе выполнение

0.57

0.36

0.07

117.24

81.53

0.00

0.00

Q42

0.82

0.58

0.18

136.13

61.56

72.99

0.00

Q42 — второе выполнение

0.58

0.60

0.12

99.95

55.43

0.00

0.00

Q43

0.88

0.64

0.18

243.94

110.67

80.21

0.00

Q43 — второе выполнение

0.63

0.47

0.08

135.68

97.21

0.00

0.00

Приложение D. Сценарий подключения к S3-хранилищу

За подробной информацией о хранилищах обратитесь к Главе 15.

Перед выполнением этого сценария:

Установите Postgres Pro AXE.

Настройте каталог pgpro_metastore.

Чтобы подключиться к S3-хранилищу:

Установите значение true для параметров конфигурации duckdb.unsafe_allow_execution_inside_functions и duckdb.convert_unsupported_numeric_to_double:

ALTER SYSTEM SET duckdb.unsafe_allow_execution_inside_functions TO true;
SET duckdb.convert_unsupported_numeric_to_double = true;

Перезагрузите сервер Postgres Pro AXE:

SELECT pg_reload_conf();

Создайте простой секрет с помощью функции duckdb.create_simple_secret.

Пример D.1.

SELECT duckdb.create_simple_secret(
      type          := 'S3',
      key_id        := 'access_key_ID_example',
      secret        := 'simple_secret_example',
      region        := '',
      url_style     := 'path',
      endpoint      := 'hostname:port',
      use_ssl       := 'false');

Создайте S3-хранилище.

Пример D.2.

  SELECT metastore.add_storage('s3_storage_example', 's3://demo/s3_storage_example/','s3://demo/s3_storage_example/tmp/');

Убедитесь, что S3-хранилище правильно настроено:

Создайте таблицу-кучу с помощью команды CREATE TABLE.

Пример D.3.

CREATE TABLE heap_table_example (id int4 NULL, "name" text NULL, price numeric(10,2) NULL, created_at timestamp NULL);

Вставьте строки в таблицу-кучу с помощью команды INSERT.

Пример D.4.

INSERT INTO heap_table_example VALUES (1, 'Item 1', 150.99, '2025-08-09 00:01:01.75');
INSERT INTO heap_table_example VALUES (2, 'Item 2', 300.99, '2025-08-10 00:01:01.75');

Создайте аналитическую таблицу из таблицы-кучи.

Пример D.5.

SELECT metastore.add_table('analytical_table_example', 's3_storage_example', 'public.heap_table_example', '');

Скопируйте OLAP-данные в аналитическую таблицу из таблицы-кучи.

Пример D.6.

SELECT metastore.copy_table('analytical_table_example', $$select * from heap_table_example$$);

Создайте представление Postgres Pro из аналитической таблицы.

Пример D.7.

SELECT metastore.create_view('analytical_table_example');

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

Пример D.8.

SELECT * from analytical_table_example;

Приложение E. Примеры использования снимков

Пример E.1.

Выполнение ETL-операции add_storage создаёт новое хранилище в таблице метаданных pga_storage. Для этого изменения создаётся новый снимок в таблице метаданных pga_snapshot. Созданные записи связываются с помощью значения begin_snapshot таблицы метаданных pga_snapshot.

Текущие значения schema_version и next_catalog_id таблицы метаданных pga_snapshot увеличиваются на один, так как были обновлены метаданные и созданы новые записи в таблицах метаданных.

Пример E.2.

Выполнение ETL-операции remove_storage деактивирует хранилище из таблицы метаданных pga_storage, но не удаляет его физически. Для этого изменения создаётся новый снимок в таблице метаданных pga_snapshot. Созданные записи связываются с помощью значения end_snapshot таблицы метаданных pga_snapshot.

Текущее значение schema_version таблицы метаданных pga_snapshot увеличивается на один, а значение next_catalog_id остаётся неизменным, так как были обновлены метаданные, но не были созданы новые записи в таблицах метаданных.

Пример E.3.

Выполнение ETL-операции add_table создаёт новую аналитическую таблицу в таблице метаданных pga_table и N новых столбцов в таблице метаданных pga_column. Для этих изменений создаётся N+1 новых снимков в таблице метаданных pga_snapshot. Созданные записи связываются с помощью значения begin_snapshot таблицы метаданных pga_snapshot.

Текущее значение schema_version таблицы метаданных pga_snapshot увеличивается на один, а значение next_catalog_id увеличивается на N+1, так как были обновлены метаданные и созданы новые записи в таблицах метаданных. Значение schema_version увеличивается только на один, потому что все изменения были выполнены в рамках одной ETL-операции.

Значение next_catalog_id+1 используется в качестве значения table_id для новой аналитической таблицы в таблице метаданных pga_table, в то время как значения next_catalog_id+1+N используются в качестве значений column_id для новых столбцов в таблице метаданных pga_column.

Пример E.4.

Выполнение ETL-операции add_files создаёт один или несколько Parquet-файлов в таблице метаданных pga_data_file. Для этого изменения создаётся N новых снимков в таблице метаданных pga_snapshot. Созданные записи связываются с помощью значения begin_snapshot таблицы метаданных pga_snapshot.

Текущие значения schema_version и next_catalog_id таблицы метаданных pga_snapshot остаются неизменными, так как не были обновлены метаданные и не были созданы новые записи в таблицах метаданных.

Значение next_file_id таблицы метаданных pga_snapshot увеличивается на один, а значения next_file_id+N используются в качестве значений data_file_id для новых Parquet-файлов в таблице метаданных pga_data_file.

Приложение F. Об OLTP и OLAP-нагрузках

Postgres Pro традиционно используется для транзакционных (OLTP) нагрузок и определяет архитектурные решения в отношении размещения данных и используемых алгоритмов обработки. OLTP-нагрузки имеют следующие характеристики:

работают с небольшими объёмами данных;

выполняют операции чтения/записи над большинством столбцов;

требуют строгих гарантий ACID;

требуют высокой скорости отклика.

Учитывая эти характеристики, оптимальным подходом является хранение и обработка данных в формате строк.

Аналитические (OLAP) нагрузки имеют значительные отличия:

обрабатывают большие объёмы данных;

выполняют только операции чтения над небольшим подмножеством столбцов;

не требуют строгих гарантий ACID;

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

С учетом этих отличий Postgres Pro AXE имеет значительное преимущество благодаря своим архитектурным особенностям:

локальное, сетевое или S3-хранилище для улучшения масштабируемости и соотношения цена/производительность;

столбцовое размещение данных в хранилище и памяти для более эффективной работы с отдельными столбцами и повышения уровня сжатия;

векторное выполнение для порционной обработки данных;

Postgres Pro AXE улучшает стандартную OLTP-функциональность Postgres Pro за счёт предоставления возможностей работать с OLAP-нагрузками и HTAP-нагрузками (HTAP — Hybrid Transactional/Analytical Processing, гибридная транзакционно-аналитическая обработка).

Приложение G. Типы данных

G.1. Типы данных в pgpro_axe

pgpro_axe может читать множество типов данных, существующих как в Postgres Pro, так и в DuckDB. В настоящий момент в запросах можно использовать следующие типы данных:

целочисленные типы (например, integer, bigint)

типы с плавающей точкой (например, real, double precision)

numeric

Внутри может быть конвертирован в double precision. За подробной информацией обратитесь к «Известные ограничения»).

text/varchar/bpchar

bit-типы, включая битовые массивы фиксированных и переменных размеров

bytea/blob

timestamp/timestamptz/date/interval/timestamp_ns/timestamp_ms/timestamp_s

boolean

uuid

json/jsonb

domain

arrays для всех типов выше

За подробной информацией об ограничениях, связанных с многомерными массивами, обратитесь к «Известные ограничения».

G.1.1. Известные ограничения

В настоящее время pgpro_axe не поддерживает все типы данных. Вы можете столкнуться со следующими проблемами:

Типы данных enum не поддерживаются.

Тип данных DuckDB decimal не поддерживает широкий диапазон значений, поддерживаемый типом данных Postgres Pro numeric.

Чтобы избежать ошибок при конвертации между этими двумя типами данных, внутри numeric конвертируется в double precision, если DuckDB не поддерживает требуемый уровень точности. Это может привести к потере точности значений.

Тип данных DuckDB timestamp_ns усекается до микросекунд при конвертации в тип данных Postgres Pro timestamp, что приводит к потере точности в выводе. Операции над значением timestamp_ns, например сортировка/группировка/сравнение, будут выполняться с полной точностью.

Столбцы jsonb конвертируются в столбцы json при чтении из DuckDB, так как в DuckDB отсутствует тип данных jsonb.

Многие функции и операторы Postgres Pro json и jsonb не реализованы в DuckDB. Вместо них можно использовать функции и операции DuckDB json.

За подробной информацией об этих функциях обратитесь к официальной документации DuckDB.

В Postgres Pro тип данных DuckDB tinyint конвертируется в char. Это объясняется тем, что Postgres Pro не поддерживает тип данных tinyint. В результате вместо стандартного числа отображается hex-код.

Могут возникать проблемы при преобразовании между многомерными массивами Postgres Pro и вложенными списками DuckDB (LIST) в DuckDB, так как функциональности, поддерживаемые базами данных, не совпадают полностью.

В частности, в Postgres Pro массивы в столбце могут иметь разное количество измерений, например [1] и [[1], [2]] могут встретиться в одном столбце. Это запрещено в DuckDB, и степень вложенности не может изменяться. С другой стороны, в DuckDB разные списки на одном уровне вложенности могут содержать разное количество элементов, например [[1], [1, 2]]. Это запрещено в Postgres Pro. Таким образом, преобразование между этими типами возможно, только если массивы следуют за подмножеством.

Другой возможной проблемой может быть то, что pgpro_axe использует метаданные столбцов Postgres Pro для определения количества измерений массива. Так как Postgres Pro не выдаёт ошибку, когда вы добавляете массивы с разными измерениями, возможна ситуация, при которой количество измерений в столбце метаданных не соответствует реальному количеству измерений. Чтобы избежать этого, вам нужно изменить тип данных столбца:

    -- преобразует столбец в трёхмерную текстовую строку --
    ALTER TABLE s ALTER COLUMN a SET DATA TYPE text[][][];

Для типа domain при выполнении операции INSERT проверка domain проводится Postgres Pro, а не DuckDB. Если выполняется операция SELECT и запрашиваемое поле имеет тип данных domain, его потребуется преобразовать в соответствующий базовый тип и обработать с помощью DuckDB.

G.2. Специальные типы данных в pgpro_axe

В pgpro_axe представлено несколько специальных типов данных Postgres Pro. Эти типы не нужно создавать в явном виде, но периодически они могут отображаться в сообщениях об ошибках от Postgres Pro.

duckdb.row

Тип duckdb.row возвращается такими функциями, как read_parquet, read_csv и scan_iceberg. В зависимости от аргументов эти функции могут возвращать строки с различными столбцами и типами данных. В настоящее время Postgres Pro не полностью поддерживает эти функции, поэтому они возвращают специальный тип. Чтобы получить столбцы этих строк, необходимо использовать синтаксис «square bracket indexing» (индексация квадратных скобок), как если бы вы хотели получить поле:

Пример G.1.

      SELECT r['id'], r['name'] FROM read_parquet('file.parquet') r WHERE r['age'] > 21;

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

Пример G.2.

      SELECT * FROM read_parquet('file.parquet');

Существуют ограничения по использованию функции, которая возвращает duckdb.row в CTE или подзапросе. Основная проблема заключается в том, что pgpro_axe не может автоматически назначать нужные псевдонимы выбранным столбцам строки.

Без CTE или подзапроса такой запрос возвращает столбец r[company] как company:

Пример G.3.

      SELECT r['company']
      FROM duckdb.query($$ SELECT 'DuckDB Labs' company $$) r;
      --    company
      -- ─────────────
      --  DuckDB Labs

Такой же запрос в подзапросе или CTE возвращает столбец как r:

Пример G.4.

      WITH mycte AS (
      SELECT r['company']
      FROM duckdb.query($$ SELECT 'DuckDB Labs' company $$) r
      )
      SELECT * FROM mycte;
      --       r
      -- ─────────────
      --  DuckDB Labs

Это можно легко обойти, добавив явный псевдоним столбцу в CTE или подзапросе:

Пример G.5.

      WITH mycte AS (
      SELECT r['company'] AS company
      FROM duckdb.query($$ SELECT 'DuckDB Labs' company $$) r
      )
      SELECT * FROM mycte;
      --    company
      -- ─────────────
      --  DuckDB Labs

Другим ограничением является то, что если при использовании SELECT * в CTE или подзапросе необходимо указать конкретный столбец вне CTE или подзапроса, вам всё равно потребуется использовать синтаксис r['имя_столбца'] вместо имя_столбца.

Хотя такой запрос работает нормально:

Пример G.6.

      WITH mycte AS (
      SELECT *
      FROM duckdb.query($$ SELECT 'DuckDB Labs' company $$) r
      )
      SELECT * FROM mycte;
      --    company
      -- ─────────────
      --  DuckDB Labs

При выполнении следующего запроса происходит ошибка:

Пример G.7.

      WITH mycte AS (
      SELECT *
      FROM duckdb.query($$ SELECT 'DuckDB Labs' company $$) r
      )
      SELECT * FROM mycte WHERE company = 'DuckDB Labs';
      -- ERROR:  42703: column "company" does not exist
      -- LINE 5: SELECT * FROM mycte WHERE company = 'DuckDB Labs';
      --                                   ^
      -- HINT:  If you use DuckDB functions like read_parquet, you need to use the r['colname'] syntax to use columns. If you are already doing that, maybe you forgot to to give the function the r alias.

Это можно легко обойти с помощью следующего синтаксиса r['имя_столбца']:

Пример G.8.

      > WITH mycte AS (
      SELECT *
      FROM duckdb.query($$ SELECT 'DuckDB Labs' company $$) r
      )
      SELECT * FROM mycte WHERE r['company'] = 'DuckDB Labs';
      --    company
      -- ─────────────
      --  DuckDB Labs

duckdb.unresolved_type

Тип данных duckdb.unresolved_type используется, чтобы позволить Postgres Pro понять выражение, содержащее тип данных, который неизвестен в момент разбора запроса. Это может быть тип любого столбца, извлечённого из duckdb.row с помощью синтаксиса r['имя_столбца']. Многие операторы и агрегатные функции возвращают duckdb.unresolved_type, если одна из сторон оператора имеет тип данных duckdb.unresolved_type, например r['age'] + 10.

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

Вы можете столкнуться с ошибками, в которых сообщается о том, что определённые функции и операции не существуют для типа данных duckdb.unresolved_type:

Пример G.9.

      ERROR:  function some_func(duckdb.unresolved_type) does not exist
      LINE 6:  some_func(r['somecol']) as somecol

В таких случаях обходное решение — добавить явное преобразование в принимаемый функцией тип данных, например some_func(r['somecol']::text) as somecol.

duckdb.json

Тип данных duckdb.json используется для аргументов JSON-функций DuckDB. Он позволяет этим функциям принимать значения json, jsonb и duckdb.unresolved_type.

G.3. Типы данных в pgpro_metastore

В следующей таблице описаны типы данных, поддерживаемые pgpro_metastore.

Тип данных

Значение

boolean

true или false.

int8

Целое число в диапазоне от -128 до 127.

int16

Целое число в диапазоне от -32 768 до 32 767.

int32

Целое число в диапазоне от -2 147 483 648 до 2 147 483 647.

int64

Целое число в диапазоне от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807.

unit8

Целое число в диапазоне от 0 до 255.

unit16

Целое число в диапазоне от 0 до 65 535.

unit32

Целое число в диапазоне от 0 до 4 294 967 295.

unit64

Целое число в диапазоне от 0 до 18 446 744 073 709 551 615.

float32

Дробное число в диапазоне примерно от 1,17549435E-38 до 3,40282347E+38.

Точность: 6–7 знаков после запятой.

float64

Дробное число с плавающей запятой в диапазоне примерно от 4,9E-324 до 1,8E+308.

Точность: 14–15 знаков после запятой.

десятичное_число

Дробное число с фиксированной точностью, например decimal(5,2) — 123,45 или 999,99.

time

Время, например 13:00:00.

timetz

Время с указанием часового пояса, например 10:30:00+03.

date

Дата, например 2023-05-13.

timestamp

Временная метка, например 2023-05-15 14:30:00.

timestamptz

Временная метка с указанием часового пояса, например 2023-05-15 14:30:00+03.

timestamp_s

Временная метка с точностью до секунд, например 2023-05-15 14:30:00+03.

timestamp_ms

Временная метка с точностью до миллисекунд, например 2023-05-15 14:30:00.123.

timestamp_ns

Временная метка с точностью до наносекунд, например 2023-05-15 14:30:00.123456789.

interval

Временная метка, например, 1 day, 1 hour, 1 minute.

varchar

Данные переменной длины, например, abcde, 12&^gh.

blob

Большой двоичный файл, например PDF, изображение, видео, аудио.

json

JSON, например {"brand": "Apple","model": "MacBook Pro","screen_size": 13.3,"ram": "16GB"}.

json

UUID, например a81bc81b-dead-4e5d-abff-90865d1e13b1.

G.4. Соответствие типов данных в таблицах Postgres Pro, DuckDB и Parquet

Postgres Pro AXE работает с данными из разных систем и объектов. Соответствие типов данных этих систем и объектов приведено в таблице ниже.

СУБД Postgres Pro

DuckDB

Parquet Physical

Parquet Logical

Parquet Converted

bigint

BIGINT

INT64

-

INT_64

bigserial

BIGINT

INT64

-

INT_64

boolean

BOOLEAN

BOOLEAN

-

-

bytea

BLOB

BYTE_ARRAY

-

-

character

VARCHAR

BYTE_ARRAY

-

UTF8

character varying

VARCHAR

BYTE_ARRAY

-

UTF8

date

DATE

INT32

-

DATE

double precision

DOUBLE

DOUBLE

-

-

integer

INTEGER

INT32

-

INT_32

interval

INTERVAL

FIXED_LEN_BYTE_ARRAY(12)

-

INTERVAL

json

JSON

BYTE_ARRAY

JsonType

JSON

jsonb

JSON

BYTE_ARRAY

JsonType

JSON

money

VARCHAR

BYTE_ARRAY

-

UTF8

numeric

DOUBLE

DOUBLE

-

-

numeric (1–9)

DECIMAL (1–9)

INT32

Decimal (1–9)

DECIMAL

numeric (10–18)

DECIMAL (10–18)

INT64

Decimal (10–18)

DECIMAL

numeric (19–38)

DECIMAL (19–38)

FIXED_LEN_BYTE_ARRAY(16)

Decimal (19–38)

DECIMAL

real

FLOAT

FLOAT

-

-

smallint

SMALLINT

INT32

-

INT_16

smallserial

SMALLINT

INT32

-

INT_16

serial

INTEGER

INT32

-

INT_32

text

VARCHAR

BYTE_ARRAY

-

UTF8

time

TIME

INT64

TimeType(MICROS)

TIME_MICROS

timetz

TIMETZ

INT64

TimeType(MICROS)

TIME_MICROS

timestamp

TIMESTAMP

INT64

TimestampType(MICROS)

TIMESTAMP_MICROS

timestamptz

TIMESTAMPTZ

INT64

TimestampType(MICROS)

TIMESTAMP_MICROS

uuid

UUID

FIXED_LEN_BYTE_ARRAY(16)

UUIDType()

-

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