Предисловие · поддержку PostgreSQL. В России такой...

147

Transcript of Предисловие · поддержку PostgreSQL. В России такой...

Предисловие

Эту небольшую книгу мы написали для тех, кто тольконачинает знакомиться с PostgreSQL. Из нее вы узнаете:

• Что вообще такое, этот PostgreSQL . . . . . . . . . . . . . . . . . . .с.2• Как установить PostgreSQL на Linux и Windows . . .с.16• Как подключиться к серверу, начать писать

SQL-запросы и применять транзакции . . . . . . . . . . . . . с.29• Как продолжить самостоятельное изучение

SQL с помощью демобазы . . . . . . . . . . . . . . . . . . . . . . . . . . . с.59• Про возможности полнотекстового поиска . . . . . . . . с.88• Про работу с данными в формате JSON . . . . . . . . . . . . с.96• Как использовать PostgreSQL в качестве

базы данных для вашего приложения . . . . . . . . . . . . . с.106• Про полезную программу pgAdmin . . . . . . . . . . . . . . . с.120• Где найти документацию и пройти обучение . . . . с.128• Как быть в курсе происходящего . . . . . . . . . . . . . . . . . . с.139• И немного про компанию Postgres Professional . с.143

Мы надеемся, что наша книга сделает ваш первый опытработы с PostgreSQL приятным и поможет влиться в со-общество пользователей этой СУБД.

Электронная версия книги доступна по адресу:postgrespro.ru/education/introbook

Желаем удачи!

1

О PostgreSQL

PostgreSQL — наиболее полнофункциональная, свобод-но распространяемая СУБД с открытым кодом. Разра-ботанная в академической среде, за долгую историюсплотившая вокруг себя широкое сообщество разработ-чиков, эта СУБД обладает всеми возможностями, необ-ходимыми большинству заказчиков. PostgreSQL активноприменяется по всему миру для создания критичныхбизнес-систем, работающих под большой нагрузкой.

Немного истории

Современный PostgreSQL ведет происхождение от про-екта POSTGRES, который разрабатывался под руковод-ством Майкла Стоунбрейкера (Michael Stonebraker), про-фессора Калифорнийского университета в Беркли. Доэтого Майкл Стоунбрейкер уже возглавлял разработ-ку INGRES — одной из первых реляционных СУБД, —и POSTGRES возник как результат осмысления преды-дущей работы и желания преодолеть ограниченностьжесткой системы типов.

Работа над проектом началась в 1985 году, и до 1988года был опубликован ряд научных статей, описыва-

2

ющих модель данных, язык запросов POSTQUEL (в товремя SQL еще не был общепризнанным стандартом)и устройство хранилища данных.

POSTGRES иногда еще относят к так называемым по-стреляционным СУБД. Ограниченность реляционной мо-дели всегда была предметом критики, хотя и являласьобратной стороной ее простоты и строгости. Однакопроникновение компьютерных технологий во все сферыжизни требовало новых приложений, а от баз данных —поддержки новых типов данных и таких возможностей,как наследование, создание сложных объектов и управ-ление ими.

Первая версия СУБД была выпущена в 1989 году. Базаданных совершенствовалась на протяжении несколь-ких лет, а в 1993 году, когда вышла версия 4.2, проектбыл закрыт. Но, несмотря на официальное прекращение,открытый код и BSD-лицензия позволили выпускникамБеркли Эндрю Ю и Джоли Чену в 1994 году взяться заего дальнейшее развитие. Они заменили язык запросовPOSTQUEL на ставший к тому времени общепринятымSQL, а проект нарекли Postgres95.

К 1996 году стало ясно, что название Postgres95 не вы-держит испытание временем, и было выбрано новоеимя — PostgreSQL, которое отражает связь и с ориги-нальным проектом POSTGRES, и с переходом на SQL.Именно поэтому PostgreSQL произносится «постгрес-ку-эль» или просто «постгрес», но только не «постгре».

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

3

себя поначалу небольшая группа инициативных поль-зователей и разработчиков, которая получила названиеГлобальной группы разработки PostgreSQL (PostgreSQLGlobal Development Group).

Развитие

Все основные решения о планах развития и выпускахновых версий принимаются Управляющим комитетом(Core team) проекта. В настоящий момент он состоитиз пяти человек.

Помимо обычных разработчиков, вносящих посильнуюлепту в развитие системы, выделяется группа основныхразработчиков (major contributors), сделавших суще-ственный вклад в развитие PostgreSQL, а также группаразработчиков, имеющих право записи в репозиторийисходного кода (committers). Состав группы разработ-чиков со временем меняется, появляются новые члены,кто-то отходит от проекта. Актуальный список разра-ботчиков поддерживается на официальном сайте Post-greSQL www.postgresql.org.

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

Большую роль в становлении и развитии PostgreSQLсыграл программист из Красноярска Вадим Михеев,

4

входивший в Управляющий комитет. Он является авто-ром таких важнейших частей системы, как многоверси-онное управление одновременным доступом (MVCC),система очистки (vacuum), журнал транзакций (WAL),вложенные запросы, триггеры. Сейчас Вадим уже не за-нимается проектом.

В настоящий момент в число основных разработчиковвходят трое представителей России — Олег Бартунов,Федор Сигаев и Александр Коротков, — основавшихв 2015 году компанию Postgres Professional. Среди круп-ных направлений выполненных ими работ можно выде-лить локализацию PostgreSQL (поддержка национальныхкодировок и Unicode), систему полнотекстового поис-ка, работу с массивами и слабо-структурированнымиданными (hstore, json, jsonb), новые методы индексации(GiST, SP-GiST, GIN и RUM, Bloom). Они являются автора-ми большого числа популярных расширений.

Цикл работы над очередной версией PostgreSQL обычнозанимает около года. За это время от всех желающихпринимаются на рассмотрение патчи с исправления-ми, изменениями и новым функционалом. Для обсужде-ния патчей по традиции используется список рассылкиpgsql-hackers. Если сообщество признает идею полез-ной, ее реализацию — правильной, а код проходит обя-зательную проверку другими разработчиками, то патчвключается в новый релиз.

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

5

Несколько раз в течение релизного цикла выпускаютсябета-версии, ближе к концу цикла появляется релиз-кандидат, а вскоре выходит и новая основная версия(major version) PostgreSQL.

Раньше номер основной версии состоял из двух чисел,но, начиная с 2017 года, было решено оставить толькоодно. Таким образом, за 9.6 последовала 10 — она и яв-ляется последней актуальной версией PostgreSQL. Сле-дующая версия, запланированная на осень 2018 года,будет иметь номер 11.

В процессе работы над новой версией находятся и ис-правляются ошибки. Наиболее критические из них ис-правляются не только в текущей, но и в предыдущихверсиях. По мере накопления таких исправлений при-нимается решение о выпуске дополнительных версий(minor version), совместимых со старыми. Например,версия 9.6.6 содержит только исправления ошибок, най-денных в 9.6, а 10.2 будет содержать исправления дляверсии 10.

Поддержка

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

6

Помимо поддержки сообществом разработчиков, рядкомпаний по всему миру осуществляет коммерческуюподдержку PostgreSQL. В России такой компанией явля-ется Postgres Professional (www.postgrespro.ru), предо-ставляя услуги по поддержке в режиме 24x7.

Современное состояние

PostgreSQL является одной из самых популярных базданных. За более чем 20-летнюю историю развитияна прочном фундаменте, заложенном академическойразработкой, PostgreSQL выросла в полноценную СУБДуровня предприятия и составляет реальную альтерна-тиву коммерческим базам данных. Чтобы убедитьсяв этом, достаточно посмотреть на важнейшие харак-теристики новейшей на сегодняшний день версии Post-greSQL 10.

Надежность и устойчивость

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

7

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

PostgreSQL позволяет работать по защищенному SSL-соединению и предоставляет большое количество ме-тодов аутентификации, включая аутентификацию по па-ролю, клиентским сертификатам, а также с помощьювнешних сервисов (LDAP, RADIUS, PAM, Kerberos).

При управлении пользователями и доступом к объектамБД предоставляются следующие возможности:

• Создание и управление пользователями и групповы-ми ролями;

• Разграничение доступа к объектам БД на уровне какотдельных пользователей, так и групп;

• Детальное управление доступом на уровне отдель-ных столбцов и строк;

• Поддержка SELinux через встроенную функциональ-ность SE-PostgreSQL (мандатное управление досту-пом).

Специальная версия PostgreSQL, выпущенная компаниейPostgres Professional, сертифицирована ФСТЭК для ис-пользования в системах обработки конфиденциальнойинформации и персональных данных.

Соответствие стандартам

По мере развития стандарта ANSI SQL его поддерж-ка постоянно добавлялась в PostgreSQL. Это относится

8

ко всем версиям стандарта: SQL-92, SQL:1999, SQL:2003,SQL:2008, SQL:2011. Поддержку стандартизованной в не-давней версии SQL:2016 работы с JSON планируется до-бавить в PostgreSQL 11. В целом PostgreSQL обеспечи-вает высокий уровень соответствия стандарту и поддер-живает 160 из 179 обязательных возможностей, а такжебольшое количество необязательных.

Поддержка транзакционности

PostgreSQL обеспечивает полную поддержку свойствACID и обеспечивает эффективную изоляцию транзак-ций. Для этого в PostgreSQL используется механизммноговерсионного управления одновременным досту-пом (MVCC). Он позволяет обходиться без блокировокво всех случаях, кроме одновременного изменения од-ной и той же строки данных в нескольких процессах.При этом читающие транзакции никогда не блокиру-ют пишущие транзакции, а пишущие — читающих. Этосправедливо и для самого строгого уровня изоляцииserializable, который, используя инновационную системуSerializable Snapshot Isolation, обеспечивает полное от-сутствие аномалий сериализации и гарантирует, что приодновременном выполнении транзакций результат бу-дет таким же, как и при последовательном выполнении.

Для разработчиков приложений

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

9

приложения любого типа:

• Возможность использования различных языков длясерверного программирования: встроенного PL/pgSQL(удобного тесной интеграцией с SQL), C для критич-ных по производительности задач, Perl, Python, Tcl,а также JavaScript, Java и других;

• Программные интерфейсы для обращения к СУБД изприложений на любом языке, включая стандартныеинтерфейсы ODBC и JDBC;

• Набор объектов баз данных, позволяющий эффектив-но реализовать логику любой сложности на сторонесервера: таблицы и индексы, ограничения целостно-сти, представления и материализованные представ-ления, последовательности, секционирование, под-запросы и with-запросы (в том числе рекурсивные),агрегатные и оконные функции, хранимые функции,триггеры и т. д.;

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

• Поддержка слабоструктурированных данных в духеNoSQL: хранилище пар «ключ-значение» hstore, xml,json (как в текстовом, так и в эффективном двоичномпредставлении jsonb);

• Подключение источников данных, включая все ос-новные СУБД, в качестве внешних таблиц по стан-дарту SQL/MED с возможностью их полноценного ис-пользования, в том числе для записи и распределен-ного выполнения запросов (Foreign Data Wrappers).

10

Масштабируемость и производительность

PostgreSQL эффективно использует современную архи-тектуру многоядерных процессоров — его производи-тельность растет практически линейно с увеличениемколичества ядер.

Начиная с версии 9.6 PostgreSQL умеет работать с дан-ными в параллельном режиме, что на сегодняшний деньвключает параллельное чтение (включая индексноесканирование), соединение и агрегации. Это еще боль-ше повышает возможности использования аппаратныхсредств для ускорения операций.

Планировщик запросов

В PostgreSQL используется планировщик запросов, осно-ванный на стоимости. Используя собираемую статистикуи учитывая в своих математических моделях как дис-ковые операции, так и время работы процессора, пла-нировщик позволяет оптимизировать самые сложныезапросы. В его распоряжении находятся все методыдоступа к данным и способы выполнения соединений,характерные для передовых коммерческих СУБД.

Возможности индексирования

В PostgreSQL реализованы различные методы индек-сирования. Помимо традиционных B-деревьев, такжедоступны:

11

• GiST — обобщенное сбалансированное дерево поис-ка, которое может применяться для данных, не до-пускающих упорядочения. Примерами могут служитьR-деревья для индексирования точек на плоскостис возможностью поиска ближайших соседей (k-NNsearch) и индексирование операции пересечения ин-тервалов;

• SP-GiST — обобщенное несбалансированное деревопоиска, основанное на разбиении области значенийна непересекающиеся вложенные области. Примера-ми могут служить дерево квадрантов и префиксноедерево;

• GIN — обобщенный инвертированный индекс. Основ-ной областью применения является полнотекстовыйпоиск, где требуется находить документы, в которыхвстречается указанные в поисковом запросе слова.Другим примером использования является поискзначений в массивах данных;

• RUM — дальнейшее развитие метода GIN для пол-нотекстового поиска. Этот индекс, доступный в видерасширения, позволяет ускорить фразовый поиск исразу выдавать результаты упорядоченными по реле-вантности;

• BRIN — индекс небольшого размера, позволяющийнайти компромисс между размером индекса и скоро-стью поиска. Эффективен на больших кластеризован-ных таблицах;

• Bloom — индекс, основанный на фильтре Блума (по-явился в версии 9.6). Такой индекс, имея очень ком-

12

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

За счет расширяемости набор доступных методов ин-дексного доступа постоянно увеличивается.

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

Несколько индексов могут автоматически объединятьсяс помощью битовой карты для ускорения доступа.

Кроссплатформенность

PostgreSQL работает на операционных системах се-мейства Unix, включая серверные и клиентские раз-новидности Linux, FreeBSD, Solaris, macOS, а также наWindows.

За счет открытого и переносимого кода на языке C Post-greSQL можно собрать на самых разных платформах,даже если для них отсутствует поддерживаемая сооб-ществом сборка.

13

Расширяемость

Расширяемость — одно из фундаментальных преиму-ществ системы, лежащее в основе архитектуры Post-greSQL. Пользователи могут самостоятельно, не меняябазовый код системы, добавлять:

• Типы данных;

• Функции и операторы для работы с новыми типами;

• Индексные методы доступа;

• Языки серверного программирования;

• Подключения к внешним источникам данных (ForeignData Wrappers);

• Загружаемые расширения.

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

• CitusDB — возможность распределения данных поразным экземплярам PostgreSQL (шардинг) и массив-но-параллельного выполнения запросов;

• PostGIS — система обработки геоинформационныхданных.

Только стандартный комплект, входящий в сборку Post-greSQL 10, содержит около полусотни расширений, до-казавших свою надежность и полезность.

14

Доступность

Лицензия PostgreSQL разрешает неограниченное ис-пользование СУБД, модификацию кода, а также вклю-чение в состав других продуктов, в том числе закрытыхи коммерческих.

Независимость

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

15

Установкаи начало работы

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

Мы будем использовать дистрибутив Postgres Pro Stan-dard 10, разработанный в нашей компании PostgresProfessional. Этот дистрибутив полностью совместимс обычной СУБД PostgreSQL и дополнительно включа-ет в себя некоторые разработки, выполненные в нашейкомпании, а также ряд возможностей, которые будут до-ступны только в следующей версии PostgreSQL.

Итак, приступим. Установка и запуск сервера PostgreSQLзависит от того, какую операционную систему вы ис-пользуете. Если у вас Windows, читайте дальше; еслиLinux семейства Debian или Ubuntu — переходите сразук с. 24.

16

Инструкцию по установке для других операционных си-стем вы найдете на сайте нашей компании:postgrespro.ru/products/download.

Если нужной вам версии там не оказалось — восполь-зуйтесь обычным дистрибутивом PostgreSQL: инструкциинаходятся по адресу www.postgresql.org/download.

Windows

Установка

Скачайте установщик СУБД с нашего сайта:repo.postgrespro.ru/pgpro-10/win.

Выберите 32- или 64-разрядную версию в зависимостиот того, какая у вас версия Windows. Запустите скачан-ный файл и выберите язык установки.

Установщик построен в традиционном стиле «мастера»:вы можете просто нажимать на кнопку «Далее», есливас устраивают предложенные варианты. Остановимсяподробнее на основных шагах.

17

Компоненты устанавливаемой программы:

Оставьте оба флажка, если не уверены, какие выбрать.

18

Выбор папки установки:

По умолчанию PostgreSQL устанавливается в каталогC:\Program Files\PostgresPro\10 (или C:\Program

Files (x86)\PostgresPro\10 для 32-разрядной версиина 64-разрядной системе).

19

Отдельно можно выбрать расположение каталога длябаз данных. Именно здесь будет находиться хранимаяв СУБД информация, так что убедитесь, что на дискедостаточно места, если вы планируете хранить многоданных.

20

Параметры сервера:

Если вы планируете хранить данные на русском языке,выберите локаль «Russian, Russia» (или оставьте вариант«Настройка ОС», если в Windows используется русскаялокаль).

Введите (и подтвердите повторным вводом) парольпользователя СУБД postgres. Также отметьте флажок«Настроить переменные среды», чтобы подключатьсяк серверу PostgreSQL под текущим пользователем ОС.

Остальные поля можно оставить со значениями по умол-чанию.

21

Если вы планируете установить PostgreSQL только дляознакомительных целей, можно отметить вариант «Ис-пользовать параметры по умолчанию», чтобы СУБД незанимала много оперативной памяти.

Управление службой и основные файлы

При установке Postgres Pro в вашей системе регистри-руется служба «postgrespro-X64-10» (или «postgrespro-X86-10» для 32-разрядной версии). Она запускается ав-томатически при старте компьютера под учетной запи-сью Network Service (Сетевая служба). При необходимо-

22

сти вы можете изменить параметры службы с помощьюстандартных средств Windows.

Чтобы временно остановить службу сервера баз дан-ных, выполните программу «Stop Server» из папки в ме-ню «Пуск», которую вы указали при установке:

Для запуска службы там же есть программа «Start Server».

Если при запуске службы произошла ошибка, для по-иска причины следует заглянуть в журнал сообщенийсервера. Он находится в подкаталоге log того катало-га, который был выбран при установке для баз данных(обычно это будет C:\Program Files\PostgresPro\10\

data\log). Журнал настроен таким образом, чтобы за-пись периодически переключалась в новый файл. Найтиактуальный файл можно по дате последнего измененияили по имени, которое содержит дату и время переклю-чения.

23

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

• postgresql.conf — основной конфигурационныйфайл, содержащий значения параметров сервера;

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

Загляните в эти файлы — они прекрасно документиро-ваны.

Теперь мы готовы подключиться к базе данных и по-пробовать некоторые команды и запросы. Переходитек разделу «Пробуем SQL» на с. 29.

Debian и Ubuntu

Установка

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

Для ОС Debian (в настоящее время поддерживаютсяверсии 7 «Wheezy», 8 «Jessie» и 9 «Stretch») выполнитев терминале следующие команды:

24

$ sudo apt-get install lsb-release

$ sudo sh -c 'echo "deb \http://repo.postgrespro.ru/pgpro-10/debian \$(lsb_release -cs) main" > \/etc/apt/sources.list.d/postgrespro.list'

Для ОС Ubuntu (в настоящее время поддерживаютсяверсии 14.04 «Trusty», 16.04 «Xenial», 16.10 «Yakkety»,17.04 «Zesty» и 17.10 «Artful») команды немного отлича-ются:

$ sudo sh -c 'echo "deb \http://repo.postgrespro.ru/pgpro-10/ubuntu \$(lsb_release -cs) main" > \/etc/apt/sources.list.d/postgrespro.list'

Дальше все одинаково для обеих систем:

$ wget --quiet -O - http://repo.postgrespro.ru/pgpro-10/keys/GPG-KEY-POSTGRESPRO | sudo apt-key add -

$ sudo apt-get update

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

$ locale

Если вы планируете хранить данные на русском языке,значение переменных LC_CTYPE и LC_COLLATE должнобыть равно «ru_RU.UTF8» (значение «en_US.UTF8» тожеподходит, но менее предпочтительно). При необходимо-сти установите эти переменные:

$ export LC_CTYPE=ru_RU.UTF8

25

$ export LC_COLLATE=ru_RU.UTF8

Также убедитесь, что в операционной системе установ-лена соответствующая локаль:

$ locale -a | grep ru_RU

ru_RU.utf8

Если это не так, сгенерируйте ее:

$ sudo locale-gen ru_RU.utf8

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

$ sudo apt-get install postgrespro-std-10

Как только эта команда выполнится, СУБД PostgreSQLбудет установлена, запущена и готова к работе. Чтобыпроверить это, выполните команду:

$ sudo -u postgres psql -c 'select now()'

Если все проделано успешно, в ответ вы должны полу-чить текущее время.

26

Управление службой и основные файлы

При установке PostgreSQL на вашей системе автомати-чески был создан специальный пользователь postgres,от имени которого работают процессы, обслуживающиесервер, и которому принадлежат все файлы, относящи-еся к СУБД. PostgreSQL будет автоматически запускатьсяпри перезагрузке операционной системы. С настройка-ми по умолчанию это не проблема: если вы не рабо-таете с сервером базы данных, он потребляет совсемнемного ресурсов вашей системы. Если вы все-таки за-хотите отключить автозапуск, выполните:

$ sudo pg-setup service disable

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

$ sudo service postgrespro-std-10 stop

Запустить службу сервера можно командой:

$ sudo service postgrespro-std-10 start

Полный список команд можно получить, выполнив:

$ sudo service postgrespro-std-10

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

27

$ sudo journalctl -xeu postgrespro-std-10

Но в некоторых старых версиях операционных системвам может потребоваться заглянуть в файл/var/lib/pgpro/std-10/pgstartup.log.

Вся информация, которая попадает в базу данных, рас-полагается в файловой системе в специальном каталоге/var/lib/pgpro/std-10/data/. Убедитесь, что у вас до-статочно свободного места, если собираетесь хранитьмного данных.

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

• /var/lib/pgpro/std-10/data/postgresql.conf —основной конфигурационный файл, содержащий зна-чения параметров сервера;

• /var/lib/pgpro/std-10/data/pg_hba.conf — файл,определяющий настройки доступа. В целях безопас-ности по умолчанию доступ разрешен только с ло-кального компьютера и только для пользователя ОСpostgres.

Самое время подключиться к базе данных и попробо-вать SQL в деле.

28

Пробуем SQL

Подключение с помощью psql

Чтобы подключиться к серверу СУБД и выполнить какие-либо команды, требуется программа-клиент. В главе«PostgreSQL для приложения» мы будем говорить о том,как посылать запросы из программ на разных языкахпрограммирования, а сейчас речь пойдет о терминаль-ном клиенте psql, работа с которым происходит интер-активно в режиме командной строки.

К сожалению, в наше время многие недолюбливают ко-мандную строку. Почему имеет смысл научиться с нейработать?

Во-первых, psql — стандартный клиент, входящий в лю-бую сборку PostgreSQL, и поэтому он всегда под рукой.Безусловно, хорошо иметь настроенную под себя сре-ду, но нет никакого резона оказаться беспомощнымв незнакомом окружении.

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

29

программного кода на сервер СУБД. Он имеет собствен-ные команды, позволяющие сориентироваться в объек-тах, хранящихся в базе данных, и удобно представитьинформацию из таблиц.

Но если вы привыкли работать с графическими поль-зовательскими интерфейсами, попробуйте pgAdmin —мы еще упомянем эту программу ниже — или другиеаналогичные продукты: wiki.postgresql.org/wiki/Community_Guide_to_PostgreSQL_GUI_Tools

Чтобы запустить psql, в операционной системе Linuxвыполните команду:

$ sudo -u postgres psql

В Windows запустите программу «SQL Shell (psql)» изпапки меню «Пуск», которую вы указали при установке:

30

В ответ на запрос введите пароль пользователя post-

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

Пользователи Windows могут столкнуться с неправиль-ным отображением русских букв в терминале. В этомслучае убедитесь, что свойствах окна терминала уста-новлен TrueType-шрифт (обычно «Lucida Console» или«Consolas»).

В итоге и в одной, и в другой операционной системе выувидите одинаковое приглашение postgres=#. «Post-gres» здесь — имя базы данных, к которой вы сейчасподключены. Один сервер PostgreSQL может одновре-менно обслуживать несколько баз данных, но одновре-менно вы работаете только с одной из них.

Дальше мы будем приводить некоторые команды. Вво-дите только то, что выделено жирным шрифтом; пригла-шение и ответ системы на команду приведены исключи-тельно для удобства.

База данных

Давайте создадим новую базу данных с именем test.Выполните:

postgres=# CREATE DATABASE test;

CREATE DATABASE

Не забудьте про точку с запятой в конце команды —

31

пока PostgreSQL не увидит этот символ, он будет счи-тать, что вы продолжаете ввод (так что команду можноразбить на несколько строк).

Теперь переключимся на созданную базу:

postgres=# \c test

You are now connected to database "test" as user"postgres".

test=#

Как вы видите, приглашение сменилось на test=#.

Команда, которую мы только что ввели, не похожа наSQL — она начинается с обратной косой черты. Так вы-глядят специальные команды, которые понимает толькоpsql (поэтому, если у вас открыт pgAdmin или другоеграфическое средство, пропускайте все, что начинаетсяна косую черту, или попытайтесь найти аналог).

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

test=# \?

Поскольку справочная информация довольна объемна,она будет показана с помощью настроенной в операци-онной системе команды-пейджера; обычно это more илиless.

32

Таблицы

В реляционных СУБД данные представляются в видетаблиц. Заголовок таблицы определяет столбцы; соб-ственно данные располагаются в строках. Данные неупорядочены (в частности, нельзя полагаться на то, чтостроки хранятся в порядке их добавления в таблицу).

Для каждого столбца устанавливается тип данных; зна-чения соответствующих полей строк должны удовлетво-рять этим типам. PostgreSQL располагает большим чис-лом встроенных типов данных (postgrespro.ru/doc/datatype.html) и возможностями для создания новых,но мы ограничимся несколькими из основных:

• integer — целые числа;

• text — текстовые строки;

• boolean — логический тип, принимающий значенияtrue (истина) или false (ложь).

Помимо обычных значений, определяемых типом дан-ных, поле может иметь неопределенное значение NULL —его можно рассматривать как «значение неизвестно»или «значение не задано».

Давайте создадим таблицу дисциплин, читаемых в вузе:

test=# CREATE TABLE courses(test(# c_no text PRIMARY KEY,test(# title text,test(# hours integertest(# );

CREATE TABLE

33

Обратите внимание, как меняется приглашение psql:это подсказка, что ввод команды продолжается на но-вой строке. (В дальнейшем для удобства мы не будемдублировать приглашение на каждой строке.)

В этой команде мы определили, что таблица с именемcourses будет состоять из трех столбцов: c_no — тек-стовый номер курса, title — название курса, и hours —целое число лекционных часов.

Кроме столбцов и типов данных мы можем опреде-лить ограничения целостности, которые будут автома-тически проверяться — СУБД не допустит появлениев базе некорректных данных. В нашем примере мыдобавили ограничение PRIMARY KEY для столбца c_no,которое говорит о том, что значения в этом столбцедолжны быть уникальными, а неопределенные значе-ния не допускаются. Такой столбец можно использо-вать для того, чтобы отличить одну строку в таблицеот других. Полный список ограничений целостности:postgrespro.ru/doc/ddl-constraints.html.

Точный синтаксис команды CREATE TABLE можно посмот-реть в документации, либо попросить справку прямов psql:

test=# \help CREATE TABLE

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

34

Наполнение таблиц

Добавим в созданную таблицу несколько строк:

test=# INSERT INTO courses(c_no, title, hours)VALUES ('CS301', 'Базы данных', 30),

('CS305', 'Сети ЭВМ', 60);

INSERT 0 2

Если вам требуется массовая загрузка данных из внеш-него источника, команда INSERT — не лучший выбор;посмотрите на специально предназначенную для этогокоманду COPY: postgrespro.ru/doc/sql-copy.html.

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

test=# CREATE TABLE students(s_id integer PRIMARY KEY,name text,start_year integer

);

CREATE TABLE

test=# INSERT INTO students(s_id, name, start_year)VALUES (1451, 'Анна', 2014),

(1432, 'Виктор', 2014),(1556, 'Нина', 2015);

INSERT 0 3

35

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

Запись в таблице экзаменов идентифицируется сово-купностью имени студента и номера курса. Такое огра-ничение целостности, относящее сразу к несколькимстолбцам, определяется с помощью фразы CONSTRAINT:

test=# CREATE TABLE exams(s_id integer REFERENCES students(s_id),c_no text REFERENCES courses(c_no),score integer,CONSTRAINT pk PRIMARY KEY(s_id, c_no)

);

CREATE TABLE

Кроме того, с помощью фразы REFERENCES мы опре-делили два ограничения ссылочной целостности, на-зываемые внешними ключами. Такие ключи показыва-ют, что значения в одной таблице ссылаются на строкив другой таблице. Теперь при любых действиях СУБДбудет проверять, что все идентификаторы s_id, ука-занные в таблице экзаменов, соответствуют реальнымстудентам (то есть записям в таблице студентов), а но-мера c_no — реальным курсам. Таким образом, будетисключена возможность оценить несуществующего сту-дента или поставить оценку по несуществующей дис-циплине — независимо от действий пользователя иливозможных ошибок в приложении.

36

Поставим нашим студентам несколько оценок:

test=# INSERT INTO exams(s_id, c_no, score)VALUES (1451, 'CS301', 5),

(1556, 'CS301', 5),(1451, 'CS305', 5),(1432, 'CS305', 4);

INSERT 0 4

Выборка данных

Простые запросы

Чтение данных из таблиц выполняется оператором SELECT.Например, выведем два столбца из таблицы courses:

test=# SELECT title AS course_title, hoursFROM courses;

course_title | hours--------------+-------Базы данных | 30Сети ЭВМ | 60(2 rows)

Конструкция AS позволяет переименовать столбец, еслиэто необходимо. Чтобы вывести все столбцы, достаточноуказать символ звездочки:

test=# SELECT * FROM courses;

c_no | title | hours-------+-------------+-------CS301 | Базы данных | 30CS305 | Сети ЭВМ | 60(2 rows)

37

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

test=# SELECT start_year FROM students;

start_year------------

201420142015

(3 rows)

Чтобы выбрать все различные года поступления, послеSELECT надо добавить слово DISTINCT:

test=# SELECT DISTINCT start_year FROM students;

start_year------------

20142015

(2 rows)

Подробнее смотрите в документации: postgrespro.ru/doc/sql-select.html#SQL-DISTINCT

Вообще после слова SELECT можно указывать и любыевыражения. А без фразы FROM результирующая таблицабудет содержать одну строку. Например:

test=# SELECT 2+2 AS result;

result--------

4(1 row)

38

Обычно при выборке данных требуется получить не всестроки, а только удовлетворяющие какому-либо усло-вию. Такое условие фильтрации записывается во фразеWHERE:

test=# SELECT * FROM courses WHERE hours > 45;

c_no | title | hours-------+----------+-------CS305 | Сети ЭВМ | 60(1 row)

Условие должно иметь логический тип. Например, ономожет содержать отношения =, <> (или !=), >, >=, <, <=;может объединять более простые условия с помощьюлогических операций AND, OR, NOT и круглых скобок —как в обычных языках программирования.

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

Учтите:

• результат сравнения чего-либо с неопределеннымзначением не определен;

• результат логических операций с неопределеннымзначением, как правило, не определен (исключения:true OR NULL = true, false AND NULL = false);

• для проверки определенности значения исполь-зуются специальные отношения IS NULL (IS NOT

NULL) и IS DISTINCT FROM (IS NOT DISTINCT FROM),

39

а также бывает удобно воспользоваться функциейcoalesce.

Подробнее смотрите в документации: postgrespro.ru/doc/functions-comparison.html

Соединения

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

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

test=# SELECT * FROM courses, exams;

c_no | title | hours | s_id | c_no | score-------+-------------+-------+------+-------+-------CS301 | Базы данных | 30 | 1451 | CS301 | 5CS305 | Сети ЭВМ | 60 | 1451 | CS301 | 5CS301 | Базы данных | 30 | 1556 | CS301 | 5CS305 | Сети ЭВМ | 60 | 1556 | CS301 | 5CS301 | Базы данных | 30 | 1451 | CS305 | 5CS305 | Сети ЭВМ | 60 | 1451 | CS305 | 5CS301 | Базы данных | 30 | 1432 | CS305 | 4CS305 | Сети ЭВМ | 60 | 1432 | CS305 | 4(8 rows)

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

40

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

test=# SELECT courses.title, exams.s_id, exams.scoreFROM courses, examsWHERE courses.c_no = exams.c_no;

title | s_id | score-------------+------+-------Базы данных | 1451 | 5Базы данных | 1556 | 5Сети ЭВМ | 1451 | 5Сети ЭВМ | 1432 | 4(4 rows)

Запросы можно формулировать и в другом виде, ука-зывая соединения с помощью ключевого слова JOIN.Выведем студентов и их оценки по курсу «Сети ЭВМ»:

test=# SELECT students.name, exams.scoreFROM studentsJOIN exams

ON students.s_id = exams.s_idAND exams.c_no = 'CS305';

name | score--------+-------Анна | 5Виктор | 4(2 rows)

С точки зрения СУБД обе формы эквивалентны, так чтоможно использовать тот способ, который представляет-ся более наглядным.

41

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

test=# SELECT students.name, exams.scoreFROM studentsLEFT JOIN exams

ON students.s_id = exams.s_idAND exams.c_no = 'CS305';

name | score--------+-------Анна | 5Виктор | 4Нина |(3 rows)

В этом примере в результат добавляются строки из ле-вой таблицы (поэтому операция называется LEFT JOIN),для которых не нашлось пары в правой. При этом длястолбцов правой таблицы возвращаются неопределен-ные значения.

Условия во фразе WHERE применяются к уже готовомурезультату соединений, поэтому, если вынести ограни-чение на дисциплины из условия соединения, Нина непопадет в выборку — ведь для нее exams.c_no не опре-делен:

42

test=# SELECT students.name, exams.scoreFROM studentsLEFT JOIN exams ON students.s_id = exams.s_idWHERE exams.c_no = 'CS305';

name | score--------+-------Анна | 5Виктор | 4(2 rows)

Не стоит опасаться соединений. Это обычная и есте-ственная для реляционных СУБД операция, и у Post-greSQL имеется целый арсенал эффективных механиз-мов для ее выполнения. Не соединяйте данные в при-ложении, доверьте эту работу серверу баз данных — онпрекрасно с ней справляется.

Подробнее смотрите в документации: postgrespro.ru/doc/sql-select.html#SQL-FROM

Подзапросы

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

Если подзапрос возвращает одну строку и один столбец,его можно использовать как обычное скалярное выра-жение:

43

test=# SELECT name,(SELECT scoreFROM examsWHERE exams.s_id = students.s_idAND exams.c_no = 'CS305')

FROM students;

name | score--------+-------Анна | 5Виктор | 4Нина |(3 rows)

Если подзапрос, использованный в списке выраженийSELECT, не содержит ни одной строки, возвращаетсянеопределенное значение (как в последней строке ре-зультата примера).

Такие скалярные подзапросы можно использовать ив условиях фильтрации. Получим все экзамены, которыесдавали студенты, поступившие после 2014 года:

test=# SELECT *FROM examsWHERE (SELECT start_year

FROM studentsWHERE students.s_id = exams.s_id) > 2014;

s_id | c_no | score------+-------+-------1556 | CS301 | 5(1 row)

В SQL можно формулировать условия и на подзапро-сы, возвращающие произвольное количество строк. Для

44

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

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

test=# SELECT name, start_yearFROM studentsWHERE s_id IN (SELECT s_id

FROM examsWHERE c_no = 'CS305');

name | start_year--------+------------Анна | 2014Виктор | 2014(2 rows)

Вариантом является отношение NOT IN, возвращающеепротивоположный результат. Например, список студен-тов, получивших только отличные оценки (то есть неполучивших более низкие оценки):

test=# SELECT name, start_yearFROM studentsWHERE s_id NOT IN (SELECT s_id

FROM examsWHERE score < 5);

name | start_year------+------------Анна | 2014Нина | 2015(2 rows)

45

Другая возможность — предикат EXISTS, проверяющий,что подзапрос возвратил хотя бы одну строку. С его по-мощью можно записать предыдущий запрос в другомвиде:

test=# SELECT name, start_yearFROM studentsWHERE NOT EXISTS (SELECT s_id

FROM examsWHERE exams.s_id = students.s_idAND score < 5);

name | start_year------+------------Анна | 2014Нина | 2015(2 rows)

Подробнее смотрите в документации: postgrespro.ru/doc/functions-subquery.html

В примерах выше мы уточняли имена столбцов назва-ниями таблиц, чтобы избежать неоднозначности. Иногдаэтого недостаточно. Например, в запросе одна и та жетаблица может участвовать два раза, или вместо табли-цы в предложении FROM мы можем использовать безы-мянный подзапрос. В этих случаях после подзапросаможно указать произвольное имя, которое называетсяпсевдонимом (alias). Псевдонимы можно использоватьи для обычных таблиц.

Имена студентов и их оценки по предмету «Базы дан-ных»:

46

test=# SELECT s.name, ce.scoreFROM students sJOIN (SELECT exams.*

FROM courses, examsWHERE courses.c_no = exams.c_noAND courses.title = 'Базы данных') ce

ON s.s_id = ce.s_id;

name | score------+-------Анна | 5Нина | 5(2 rows)

Здесь s — псевдоним таблицы, а ce — псевдоним под-запроса. Псевдонимы обычно выбирают так, чтобы онибыли короткими, но оставались понятными.

Тот же запрос можно записать и без подзапросов, на-пример, так:

test=# SELECT s.name, e.scoreFROM students s, courses c, exams eWHERE c.c_no = e.c_noAND c.title = 'Базы данных'AND s.s_id = e.s_id;

Сортировка

Как уже говорилось, данные в таблицах не упорядоче-ны, но часто бывает важно получить строки результатав строго определенном порядке. Для этого используетсяпредложение ORDER BY со списком выражений, по кото-рым надо выполнить сортировку. После каждого выра-жения (ключа сортировки) можно указать направление:

47

ASC — по возрастанию (этот порядок используется поумолчанию) или DESC — по убыванию.

test=# SELECT * FROM examsORDER BY score, s_id, c_no DESC;

s_id | c_no | score------+-------+-------1432 | CS305 | 41451 | CS305 | 51451 | CS301 | 51556 | CS301 | 5(4 rows)

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

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

Подробнее смотрите в документации: postgrespro.ru/doc/sql-select.html#SQL-ORDERBY.

Группировка

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

48

test=# SELECT count(*), count(DISTINCT s_id),avg(score)FROM exams;

count | count | avg-------+-------+--------------------

4 | 3 | 4.7500000000000000(1 row)

Аналогичную информацию можно получить в разбивкепо номерам курсов с помощью предложения GROUP BY,в котором указываются ключи группировки:

test=# SELECT c_no, count(*),count(DISTINCT s_id), vg(score)FROM examsGROUP BY c_no;

c_no | count | count | avg-------+-------+-------+--------------------CS301 | 2 | 2 | 5.0000000000000000CS305 | 2 | 2 | 4.5000000000000000(2 rows)

Полный список агрегатных функций: postgrespro.ru/doc/functions-aggregate.html.

В запросах, использующих группировку, может возник-нуть необходимость отфильтровать строки на основаниирезультатов агрегирования. Такие условия можно задатьв предложении HAVING. Отличие от WHERE состоит в том,что условия WHERE применяются до группировки (в нихможно использовать столбцы исходных таблиц), а усло-вия HAVING — после группировки (и в них можно такжеиспользовать столбцы таблицы-результата).

Выберем имена студентов, получивших более однойпятерки по любому предмету:

49

test=# SELECT students.nameFROM students, examsWHERE students.s_id = exams.s_id AND exams.score = 5GROUP BY students.nameHAVING count(*) > 1;

name------Анна(1 row)

Подробнее смотрите в документации: postgrespro.ru/doc/sql-select.html#SQL-GROUPBY.

Изменение и удаление данных

Изменение данных в таблице выполняет оператор UPDATE,в котором указываются новые значения полей для строк,определяемых предложением WHERE (таким же, как в опе-раторе SELECT).

Например, увеличим число лекционных часов для курса«Базы данных» в два раза:

test=# UPDATE coursesSET hours = hours * 2WHERE c_no = 'CS301';

UPDATE 1

Подробнее смотрите в документации: postgrespro.ru/doc/sql-update.html.

Оператор DELETE удаляет из указанной таблицы строки,определяемые все тем же предложением WHERE:

50

test=# DELETE FROM exams WHERE score < 5;

DELETE 1

Подробнее смотрите в документации: postgrespro.ru/doc/sql-delete.html.

Транзакции

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

test=# CREATE TABLE groups(g_no text PRIMARY KEY,monitor integer NOT NULL REFERENCES students(s_id)

);

CREATE TABLE

Здесь мы использовали ограничение целостности NOT

NULL, которое запрещает неопределенные значения.

Теперь в таблице студентов нам необходимо еще однополе — номер группы, — о которым мы не подумалипри создании. К счастью, в уже существующую таблицуможно добавить новый столбец:

test=# ALTER TABLE studentsADD g_no text REFERENCES groups(g_no);

ALTER TABLE

51

С помощью команды psql всегда можно посмотреть,какие поля определены в таблице:

test=# \d students

Table "public.students"Column | Type | Modifiers

------------+---------+----------s_id | integer | not nullname | text |start_year | integer |g_no | text |...

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

test=# \d

List of relationsSchema | Name | Type | Owner--------+----------+-------+----------public | courses | table | postgrespublic | exams | table | postgrespublic | groups | table | postgrespublic | students | table | postgres(4 rows)

Создадим теперь группу «A-101» и поместим в нее всехстудентов, а старостой сделаем Анну.

Тут возникает затруднение. С одной стороны, мы не мо-жем создать группу, не указав старосту. А с другой, какмы можем назначить Анну старостой, если она еще невходит в группу? Это привело бы к тому, что в базе дан-ных некоторое время (пусть и небольшое) находилисьбы логически некорректные, несогласованные данные.

52

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

Начнем транзакцию:

test=# BEGIN;

BEGIN

Затем добавим группу вместе со старостой. Посколькумы не помним наизусть номер студенческого билетаАнны, выполним запрос прямо в команде добавлениястрок:

test=# INSERT INTO groups(g_no, monitor)SELECT 'A-101', s_idFROM studentsWHERE name = 'Анна';

INSERT 0 1

Откройте теперь новое окно терминала и запустите ещеодин процесс psql: это будет сеанс, работающий парал-лельно с первым.

Чтобы не запутаться, команды второго сеанса мы будемпоказывать с отступом. Увидит ли он сделанные измене-ния?

postgres=# \c test

You are now connected to database "test" as user"postgres".

53

test=# SELECT * FROM groups;

g_no | monitor------+---------(0 rows)

Нет, не увидит, ведь транзакция еще не завершена.

Теперь переведем всех студентов в созданную группу:

test=# UPDATE students SET g_no = 'A-101';

UPDATE 3

И снова второй сеанс видит согласованные данные, ак-туальные на начало еще не оконченной транзакции:

test=# SELECT * FROM students;

s_id | name | start_year | g_no------+--------+------------+------1451 | Анна | 2014 |1432 | Виктор | 2014 |1556 | Нина | 2015 |(3 rows)

А теперь завершим транзакцию, зафиксировав все из-менения:

test=# COMMIT;

COMMIT

И только в этот момент второму сеансу становятся до-ступны все изменения, сделанные в транзакции, какбудто они появились одномоментно:

54

test=# SELECT * FROM groups;

g_no | monitor-------+---------A-101 | 1451(1 row)

test=# SELECT * FROM students;

s_id | name | start_year | g_no------+--------+------------+-------1451 | Анна | 2014 | A-1011432 | Виктор | 2014 | A-1011556 | Нина | 2015 | A-101(3 rows)

СУБД гарантирует выполнение нескольких важных свойств.

Во-первых, транзакция либо выполняется целиком (какв нашем примере), либо не выполняется совсем. Еслибы в одной из команд произошла ошибка, или мы самипрервали бы транзакцию командой ROLLBACK, то ба-за данных осталась бы в том состоянии, в котором онабыла до команды BEGIN. Это свойство называется ато-марностью.

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

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

55

которые транзакция еще не зафиксировала. Это свой-ство называется изоляцией; за счет его соблюденияСУБД способна параллельно обслуживать много сеансов,не жертвуя корректностью данных. Особенностью Post-greSQL является очень эффективная реализация изоля-ции: несколько сеансов могут одновременно читать иизменять данные, не блокируя друг друга. Блокировкавозникает только при одновременном изменении однойи той же строки двумя разными процессами.

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

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

Подробнее о транзакциях:postgrespro.ru/doc/tutorial-transactions.html

(и еще более подробно:postgrespro.ru/doc/mvcc.html).

56

Полезные команды psql

\? Справка по командам psql.

\h Справка по SQL: список доступных командили синтаксис конкретной команды.

\x Переключает обычный табличный вывод(столбцы и строки) на расширенный (каж-дый столбец на отдельной строке) и об-ратно. Удобно для просмотра нескольких«широких» строк.

\l Список баз данных.

\du Список пользователей.

\dt Список таблиц.

\di Список индексов.

\dv Список представлений.

\df Список функций.

\dn Список схем.

\dx Список установленных расширений.

\dp Список привилегий.

\d имя Подробная информация по конкретномуобъекту.

\d+ имя Еще более подробная информация по кон-кретному объекту.

\timing on Показывать время выполнения операторов.

57

Заключение

Конечно, мы успели осветить только малую толику того,что необходимо знать о СУБД, но надеемся, что вы убе-дились: начать использовать PostgreSQL совсем нетруд-но. Язык SQL позволяет формулировать запросы самойразной сложности, а PostgreSQL предоставляет каче-ственную поддержку стандарта и эффективную реализа-цию. Пробуйте, экспериментируйте!

И еще одна важная команда psql: для того, чтобы за-вершить сеанс работы, наберите

test=# \q

58

Демонстрационнаябаза данных

Описание

Общая информация

Чтобы двигаться дальше и учиться писать более слож-ные запросы, нам понадобится более серьезная базаданных — не три таблицы, а целых восемь, — и напол-нение ее данными. Схема такой базы данных изображе-на в виде диаграммы «сущность-связи» на с. 60.

В качестве предметной области мы выбрали авиапере-возки: будем считать, что речь идет о нашей (пока ещенесуществующей) авиакомпании. Тем, кто хотя бы разлетал на самолетах, эта область должна быть понятна;в любом случае мы сейчас все объясним. Хочется отме-тить, что мы старались сделать схему данных как можнопроще, не загромождая ее многочисленными деталями,но, в то же время, не слишком простой, чтобы на нейможно было учиться писать интересные и осмысленныезапросы.

59

Booki

ngs

Бро

ниро

вани

я

# bo

ok_r

ef*

book

_dat

e*

tota

l_am

ount

Tic

kets

Бил

еты

# tic

ket_

no*

book

_ref

* pa

ssen

ger_

id*

pass

enge

r_na

me

° co

nta

ct_d

ata

Aircr

afts

Сам

олет

ы

# ai

rcra

ft_c

ode

* m

odel

* ra

nge

Sea

tsМ

еста

# ai

rcra

ft_c

ode

# se

at_n

o*

fare

_con

ditio

ns

Tic

ket_

flig

hts

Пер

елет

ы

# tic

ket_

no#

fligh

t_id

* fa

re_c

ondi

tions

* a

mou

nt

Boar

din

g_p

ass

esП

осад

очны

е та

лон

ы

# tic

ket_

no#

fligh

t_id

* b

oard

ing

_no

* se

at_n

o

Flights

Рей

сы

# fli

ght_

id*

fligh

t_n

o*

sche

dule

d_de

part

ure

* sc

hedu

led_

arri

val

* de

part

ure_

airp

ort

* ar

rival

_airp

ort

* st

atus

* ai

rcra

ft_c

ode

° ac

tua

l_de

part

ure

° ac

tua

l_ar

rival

Airports

Аэр

опор

ты

# ai

rpor

t_co

de*

airp

ort_

nam

e*

city

* co

ordi

nate

s*

timez

one

60

Итак, основной сущностью является бронирование (book-ings).

В одно бронирование можно включить несколько пас-сажиров, каждому из которых выписывается отдельныйбилет (tickets). Как таковой пассажир не является от-дельной сущностью: для простоты можно считать, чтовсе пассажиры уникальны.

Каждый билет включает один или несколько перелетов(ticket_flights). Несколько перелетов могут включатьсяв билет в нескольких случаях:

1. Нет прямого рейса, соединяющего пункты отправ-ления и назначения (полет с пересадками);

2. Взят билет «туда и обратно».

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

Каждый рейс (flights) следует из одного аэропорта (air-ports) в другой. Рейсы с одним номером имеют одина-ковые пункты вылета и назначения, но будут отличатьсядатой отправления

При регистрации на рейс пассажиру выдается посадоч-ный талон (boarding_passes), в котором указано местов самолете. Пассажир может зарегистрироваться толькона тот рейс, который есть у него в билете. Комбинациярейса и места в самолете должна быть уникальной, что-бы не допустить выдачу двух посадочных талонов наодно место.

61

Количество мест (seats) в самолете и их распределениепо классам обслуживания зависит от конкретной моде-ли самолета (aircrafts), выполняющего рейс. Предполага-ется, что каждая модель имеет только одну компоновкусалона. Схема данных не контролирует, что места в по-садочных талонах соответствуют имеющимся в салоне.

Далее мы подробно опишем каждую из таблиц, а так-же дополнительные представления и функции. Точноеопределение любой таблицы, включая типы данныхи описание столбцов, вы всегда можете получить ко-мандой \d+.

Бронирование

Намереваясь воспользоваться услугами нашей авиа-компании, пассажир заранее (book_date, максимум замесяц до рейса) бронирует необходимые билеты. Бро-нирование идентифицируется своим номером book_ref

(шестизначная комбинация букв и цифр).

Поле total_amount хранит общую стоимость включен-ных в бронирование перелетов всех пассажиров.

Билет

Билет имеет уникальный номер ticket_no, состоящийиз 13 цифр.

62

Билет содержит номер документа, удостоверяющеголичность пассажира passenger_id, а также его фами-лию и имя passenger_name и контактную информациюcontact_data.

Заметим, что ни идентификатор пассажира, ни имя неявляются постоянными (можно поменять паспорт, мож-но сменить фамилию). Поэтому однозначно найти всебилеты одного и того же пассажира невозможно. Дляпростоты можно считать, что все пассажиры уникальны.

Перелет

Перелет соединяет билет с рейсом и идентифицируетсядвумя их номерами.

Для каждого перелета указываются его стоимость amountи класс обслуживания fare_conditions.

Рейс

Естественный ключ таблицы рейсов состоит из двух по-лей — номера рейса flight_no и даты отправленияscheduled_departure. Чтобы сделать внешние ключина эту таблицу компактнее, в качестве первичного ис-пользуется суррогатный ключ flight_id.

Рейс всегда соединяет две точки — аэропорты вылетаdeparture_airport и прибытия arrival_airport.

63

64

Такое понятие, как «рейс с пересадками» отсутствует:если из одного аэропорта до другого нет прямого рей-са, в билет просто включаются несколько необходимыхрейсов.

У каждого рейса есть запланированные дата и времявылета scheduled_departure и прибытия scheduled_ar-

rival. Реальные время вылета actual_departure иприбытия actual_arrival могут отличаться: обычноне сильно, но иногда и на несколько часов, если рейсзадержан.

Статус рейса status может принимать одно из следую-щих значений:

• ScheduledРейс доступен для бронирования. Это происходитза месяц до плановой даты вылета; до этого записьо рейсе не существует в базе данных.

• On TimeРейс доступен для регистрации (за сутки до плано-вой даты вылета) и не задержан.

• DelayedРейс доступен для регистрации (за сутки до плано-вой даты вылета), но задержан.

• DepartedСамолет уже вылетел и находится в воздухе.

• ArrivedСамолет прибыл в пункт назначения.

• CancelledРейс отменен.

65

Аэропорт

Каждый аэропорт идентифицируется трехбуквеннымкодом airport_code и имеет название airport_name.

Название города city указывается как атрибут аэропор-та; отдельной сущности для него не предусмотрено. На-звание можно использовать для того, чтобы определитьаэропорты одного города. Также указываются коорди-наты (долгота и широта) coordinates и часовой поясtimezone.

Посадочный талон

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

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

Самолет

Каждая модель воздушного судна идентифицируетсясвоим трехзначным кодом aircraft_code. Указываетсятакже название модели model и максимальная даль-ность полета в километрах range.

66

Место

Места определяют схему салона каждой модели. Каж-дое место определяется своим номером seat_no и име-ет закрепленный за ним класс обслуживания fare_condi-

tions — Economy, Comfort или Business.

Представление для рейсов

Над таблицей flights создано представление flights_v,содержащее дополнительную информацию:

• расшифровку данных об аэропорте вылетаdeparture_airport, departure_airport_name,departure_city,

• расшифровку данных об аэропорте прибытияarrival_airport, arrival_airport_name, arrival_city,

• местное время вылетаscheduled_departure_local, actual_departure_local,

• местное время прибытияscheduled_arrival_local, actual_arrival_local,

• продолжительность полетаscheduled_duration, actual_duration.

Представление для маршрутов

Таблица рейсов содержит избыточность: из нее мож-но было бы выделить информацию о маршруте (номер

67

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

Именно такая информация и составляет представле-ние routes. Кроме того, это представление показываетмассив дней недели days_of_week, по которым совер-шаются полеты, и плановую продолжительность рейсаduration.

Функция now

Демонстрационная база содержит временной «срез»данных — так, как будто в некоторый момент была сде-лана резервная копия реальной системы. Например, ес-ли некоторый рейс имеет статус Departed, это означает,что в момент резервного копирования самолет вылетели находился в воздухе.

Позиция «среза» сохранена в функции bookings.now.Ей можно пользоваться в запросах там, где в обычнойжизни использовалась бы функция now.

Кроме того, значение этой функции определяет версиюдемонстрационной базы данных. Актуальная версия намомент подготовки этого выпуска книги — от 15.08.2017.

68

Установка

Установка с сайта

База данных доступна в трех версиях, которые отлича-ются только объемом данных:

• edu.postgrespro.ru/demo-small.zip — небольшая,данные по полетам за один месяц (файл 21 МБ,размер БД 280 МБ),

• edu.postgrespro.ru/demo-medium.zip — средняя,данные по полетам за три месяца (файл 62 МБ,размер БД 702 МБ),

• edu.postgrespro.ru/demo-big.zip — большая,данные по полетам за один год (файл 232 МБ,размер БД 2638 МБ).

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

Файлы содержат логическую резервную копию базыdemo, созданную утилитой pg_dump. Имейте в виду, чтоесли у вас уже существует база данных demo, она будетудалена и создана заново при восстановлении из ре-зервной копии. Владельцем базы demo станет тот поль-зователь СУБД, под которым выполнялось восстановле-ние.

69

Чтобы установить демонстрационную базу данных в Li-nux, скачайте один из файлов, предварительно пере-ключившись на пользователя postgres. Например, длябазы небольшого размера:

$ sudo su - postgres

$ wget https://edu.postgrespro.ru/demo-small.zip

Затем выполните команду:

$ zcat demo-small.zip | psql

В Windows любым веб-браузером скачайте с сайта файлedu.postgrespro.ru/demo-small.zip , дважды клик-ните на нем, чтобы открыть архив, и скопируйте файлdemo-small-20170815.sql в каталог C:\Program Files\

PostgresPro10.

Затем запустите psql (ярлык «SQL Shell (psql)») и выпол-ните команду:

postgres# \i demo-small-20170815.sql

(Если файл не будет найден, проверьте свойство «Startin» («Рабочая папка») ярлыка — файл должен находить-ся именно в этом каталоге.)

70

Примеры запросов

Пара слов о схеме

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

postgres=# \c demo

You are now connected to database "demo" as user"postgres".

demo=#

Все интересующие нас объекты находятся в схеме book-

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

demo=# SELECT * FROM aircrafts;

aircraft_code | model | range---------------+---------------------+-------773 | Боинг 777-300 | 11100763 | Боинг 767-300 | 7900SU9 | Сухой Суперджет-100 | 3000320 | Аэробус A320-200 | 5700321 | Аэробус A321-200 | 5600319 | Аэробус A319-100 | 6700733 | Боинг 737-300 | 4200CN1 | Сессна 208 Караван | 1200CR2 | Бомбардье CRJ-200 | 2700(9 rows)

Однако для функции bookings.now все равно необходи-мо указывать схему, чтобы отличать ее от стандартнойфункции now:

71

demo=# SELECT bookings.now();

now------------------------2017-08-15 18:00:00+03(1 row)

Как вы заметили, названия самолетов выводятся по-русски. Так же обстоит дело и с названиями городови аэропортов:

demo=# SELECT airport_code, cityFROM airports LIMIT 5;

airport_code | city--------------+--------------------------YKS | ЯкутскMJZ | МирныйKHV | ХабаровскPKC | Петропавловск-КамчатскийUUS | Южно-Сахалинск(5 rows)

Если вы предпочитаете английские названия, установи-те параметр bookings.lang в значение en. Это можносделать, например, так:

demo=# ALTER DATABASE demo SET bookings.lang = en;

ALTER DATABASE

demo=# \c

You are now connected to database "demo" as user"postgres".

72

demo=# SELECT airport_code, cityFROM airports LIMIT 5;

airport_code | city--------------+-------------------YKS | YakutskMJZ | MirnyjKHV | KhabarovskPKC | PetropavlovskUUS | Yuzhno-sakhalinsk(5 rows)

Как это устроено, вы можете разобраться, посмотревопределение aircrafts или airports с помощью ко-манды psql \d+.

Подробнее про управление схемами:postgrespro.ru/doc/ddl-schemas.html

и про установку конфигурационных параметров:postgrespro.ru/doc/config-setting.html.

Простые запросы

Ниже мы покажем некоторые примеры задач на демон-страционной схеме. Большинство из них приведенывместе с решениями, остальные предлагается решитьсамостоятельно.

Задача. Кто летел позавчера рейсом Москва (SVO) — Но-восибирск (OVB) на месте 1A, и когда он забронировалсвой билет?

73

Решение. «Позавчера» отсчитывается от booking.now,а не от текущей даты.

SELECT t.passenger_name,b.book_date

FROM bookings bJOIN tickets t

ON t.book_ref = b.book_refJOIN boarding_passes bp

ON bp.ticket_no = t.ticket_noJOIN flights f

ON f.flight_id = bp.flight_idWHERE f.departure_airport = 'SVO'AND f.arrival_airport = 'OVB'AND f.scheduled_departure::date =

bookings.now()::date - INTERVAL '2 day'AND bp.seat_no = '1A';

Задача. Сколько мест осталось незанятыми вчера нарейсе PG0404?

Решение. Задачу можно решить несколькими спосо-бами. Первый вариант использует конструкцию NOT

EXISTS, чтобы определить места, на которые нет поса-дочных талонов:

SELECT count(*)FROM flights f

JOIN seats sON s.aircraft_code = f.aircraft_code

WHERE f.flight_no = 'PG0404'AND f.scheduled_departure::date =

bookings.now()::date - INTERVAL '1 day'AND NOT EXISTS (

SELECT NULLFROM boarding_passes bpWHERE bp.flight_id = f.flight_idAND bp.seat_no = s.seat_no

);

74

Второй вариант использует операцию вычитания мно-жеств:

SELECT count(*)FROM (

SELECT s.seat_noFROM seats sWHERE s.aircraft_code = (

SELECT aircraft_codeFROM flightsWHERE flight_no = 'PG0404'AND scheduled_departure::date =

bookings.now()::date - INTERVAL '1 day')EXCEPTSELECT bp.seat_noFROM boarding_passes bpWHERE bp.flight_id = (

SELECT flight_idFROM flightsWHERE flight_no = 'PG0404'AND scheduled_departure::date =

bookings.now()::date - INTERVAL '1 day')

) t;

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

75

Задача. На каких маршрутах произошли самые длитель-ные задержки рейсов? Выведите список из десяти «ли-деров».

Решение. В запросе надо учитывать только рейсы, кото-рые уже вылетели:

SELECT f.flight_no,f.scheduled_departure,f.actual_departure,f.actual_departure - f.scheduled_departureAS delay

FROM flights fWHERE f.actual_departure IS NOT NULLORDER BY f.actual_departure - f.scheduled_departure

DESCLIMIT 10;

То же самое условие можно записать и на основе столб-ца status.

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

Задача. Какова минимальная и максимальная продол-жительность полета для каждого из возможных рейсовиз Москвы в Санкт-Петербург, и сколько раз вылет рейсабыл задержан больше, чем на час?

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

76

SELECT f.flight_no,f.scheduled_duration,min(f.actual_duration),max(f.actual_duration),sum(CASE

WHEN f.actual_departure >f.scheduled_departure +INTERVAL '1 hour'

THEN 1 ELSE 0END) delays

FROM flights_v fWHERE f.departure_city = 'Москва'AND f.arrival_city = 'Санкт-Петербург'AND f.status = 'Arrived'GROUP BY f.flight_no,

f.scheduled_duration;

Задача. Найдите самых дисциплинированных пассажи-ров, которые зарегистрировались на все рейсы первы-ми. Учтите только тех пассажиров, которые совершалиминимум два рейса.

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

SELECT t.passenger_name,t.ticket_no

FROM tickets tJOIN boarding_passes bp

ON bp.ticket_no = t.ticket_noGROUP BY t.passenger_name,

t.ticket_noHAVING max(bp.boarding_no) = 1AND count(*) > 1;

77

Задача. Сколько человек бывает включено в одно бро-нирование?

Решение. Сначала посчитаем количество человек в каж-дом бронировании, а затем число бронирований длякаждого количества человек.

SELECT tt.cnt,count(*)

FROM (SELECT t.book_ref,

count(*) cntFROM tickets tGROUP BY t.book_ref

) ttGROUP BY tt.cntORDER BY tt.cnt;

Оконные функции

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

Решение. Используем оконные функции, чтобы не обра-щаться к одним и тем же данным два раза.

Глядя в результаты приведенного ниже запроса, мож-но обратить внимание, что запас времени в некоторыхслучаях составляет несколько дней. Как правило, этобилеты, взятые туда и обратно, то есть мы видим ужене время пересадки, а время нахождения в пункте на-значения. Используя решение одной из задач в разделе«Массивы», можно учесть этот факт в запросе.

78

SELECT tf.ticket_no,f.departure_airport,f.arrival_airport,f.scheduled_arrival,lead(f.scheduled_departure) OVER w

AS next_departure,lead(f.scheduled_departure) OVER w -

f.scheduled_arrival AS gapFROM bookings b

JOIN tickets tON t.book_ref = b.book_ref

JOIN ticket_flights tfON tf.ticket_no = t.ticket_no

JOIN flights fON tf.flight_id = f.flight_id

WHERE b.book_date =bookings.now()::date - INTERVAL '7 day'

WINDOW w AS (PARTITION BY tf.ticket_noORDER BY f.scheduled_departure);

Задача. Какие сочетания имени и фамилии встречаютсячаще всего и какую долю от числа всех пассажиров онисоставляют?

Решение. Оконная функция используется для подсчетаобщего числа пассажиров.

SELECT passenger_name,round( 100.0 * cnt / sum(cnt) OVER (), 2)AS percent

FROM (SELECT passenger_name,

count(*) cntFROM ticketsGROUP BY passenger_name

) tORDER BY percent DESC;

79

Задача. Решите предыдущую задачу отдельно для имени отдельно для фамилий.

Решение. Приведем вариант для имен.

WITH p AS (SELECT left(passenger_name,

position(' ' IN passenger_name))AS passenger_name

FROM tickets)SELECT passenger_name,

round( 100.0 * cnt / sum(cnt) OVER (), 2)AS percent

FROM (SELECT passenger_name,

count(*) cntFROM pGROUP BY passenger_name

) tORDER BY percent DESC;

Вывод: не стоит объединять в одном текстовом поленесколько значений, если вы собираетесь работать с ни-ми по отдельности; по-научному это называется «первойнормальной формой».

Массивы

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

80

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

WITH t AS (SELECT ticket_no,

a,a[1] departure,a[cardinality(a)] last_arrival,a[cardinality(a)/2+1] middle

FROM (SELECT t.ticket_no,

array_agg( f.departure_airportORDER BY f.scheduled_departure) ||

(array_agg( f.arrival_airportORDER BY f.scheduled_departure DESC)

)[1] AS aFROM tickets t

JOIN ticket_flights tfON tf.ticket_no = t.ticket_no

JOIN flights fON f.flight_id = tf.flight_id

GROUP BY t.ticket_no) t

)SELECT t.ticket_no,

t.a,t.departure,CASE

WHEN t.departure = t.last_arrivalTHEN t.middle

ELSE t.last_arrivalEND arrival,(t.departure = t.last_arrival) return_ticket

FROM t;

81

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

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

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

Решение. Часть задачи по построению массива днейнедели уже фактически решена в представлении routes.Остается только найти пересечение с помощью опера-ции &&:

SELECT r1.departure_airport,r1.arrival_airport,r1.days_of_week dow,r2.days_of_week dow_back

FROM routes r1JOIN routes r2

ON r1.arrival_airport = r2.departure_airportAND r1.departure_airport = r2.arrival_airport

WHERE NOT (r1.days_of_week && r2.days_of_week);

Рекурсивные запросы

Задача. Как с помощью минимального числа пересадокможно долететь из Усть-Кута (UKX) в Нерюнгри (CNN),и какое время придется провести в воздухе?

82

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

WITH RECURSIVE p(last_arrival,destination,hops,flights,flight_time,found

) AS (SELECT a_from.airport_code,

a_to.airport_code,array[a_from.airport_code],array[]::char(6)[],interval '0',a_from.airport_code = a_to.airport_code

FROM airports a_from,airports a_to

WHERE a_from.airport_code = 'UKX'AND a_to.airport_code = 'CNN'UNION ALLSELECT r.arrival_airport,

p.destination,(p.hops || r.arrival_airport)::char(3)[],(p.flights || r.flight_no)::char(6)[],p.flight_time + r.duration,bool_or(r.arrival_airport = p.destination)

OVER ()FROM p

JOIN routes rON r.departure_airport = p.last_arrival

WHERE NOT r.arrival_airport = ANY(p.hops)AND NOT p.found

)SELECT hops,

flights,flight_time

FROM pWHERE p.last_arrival = p.destination;

83

Зацикливание предотвращается проверкой по массивупересадок hops.

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

Поучительно сравнить скорость выполнения этого за-проса с более простым вариантом без флага.

Подробно про рекурсивные запросы вы можете посмот-реть в документации: postgrespro.ru/doc/queries-with.html

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

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

Конечно, так можно поступить, только если граф марш-рутов является связным.

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

84

WITH RECURSIVE p(departure,last_arrival,destination,hops,found

) AS (SELECT a_from.airport_code,

a_from.airport_code,a_to.airport_code,array[a_from.airport_code],a_from.airport_code = a_to.airport_code

FROM airports a_from,airports a_to

UNION ALLSELECT p.departure,

r.arrival_airport,p.destination,(p.hops || r.arrival_airport)::char(3)[],bool_or(r.arrival_airport = p.destination)

OVER (PARTITION BY p.departure,p.destination)

FROM pJOIN routes r

ON r.departure_airport = p.last_arrivalWHERE NOT r.arrival_airport = ANY(p.hops)AND NOT p.found

)SELECT max(cardinality(hops)-1)FROM pWHERE p.last_arrival = p.destination;

Задача. Найдите кратчайший путь из Усть-Кута (UKX)в Нерюнгри (CNN) с точки зрения чистого времени пе-релетов (игнорируя время пересадок).

Подсказка: этот путь может оказаться не оптимальнымпо числу пересадок.

85

Решение.

WITH RECURSIVE p(last_arrival,destination,hops,flights,flight_time,min_time

) AS (SELECT a_from.airport_code,

a_to.airport_code,array[a_from.airport_code],array[]::char(6)[],interval '0',NULL::interval

FROM airports a_from,airports a_to

WHERE a_from.airport_code = 'UKX'AND a_to.airport_code = 'CNN'UNION ALLSELECT r.arrival_airport,

p.destination,(p.hops || r.arrival_airport)::char(3)[],(p.flights || r.flight_no)::char(6)[],p.flight_time + r.duration,least(

p.min_time, min(p.flight_time+r.duration)FILTER (

WHERE r.arrival_airport = p.destination) OVER ()

)FROM p

JOIN routes rON r.departure_airport = p.last_arrival

WHERE NOT r.arrival_airport = ANY(p.hops)AND p.flight_time + r.duration <

coalesce(p.min_time, INTERVAL '1 year'))

86

SELECT hops,flights,flight_time

FROM (SELECT hops,

flights,flight_time,min(min_time) OVER () min_time

FROM pWHERE p.last_arrival = p.destination

) tWHERE flight_time = min_time;

Функции и расширения

Задача. Найдите расстояние между Калининградом (KGD)и Петропавловском-Камчатским (PKV).

Решение. У нас имеются координаты аэропортов. Чтобырассчитать расстояние, можно воспользоваться расши-рением earthdistance (и перевести результат из милейв километры).

CREATE EXTENSION IF NOT EXISTS cube;

CREATE EXTENSION IF NOT EXISTS earthdistance;

SELECT round((a_from.coordinates <@> a_to.coordinates) *1.609344

)FROM airports a_from,

airports a_toWHERE a_from.airport_code = 'KGD'AND a_to.airport_code = 'PKC';

Задача. Нарисуйте граф рейсов между аэропортами.

87

Дополнительныевозможности

Полнотекстовый поиск

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

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

88

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

Недостатки у все более популярных документо-ориен-тированных СУБД обычно в той же области: у них естьразвитые средства полнотекстового поиска, но безопас-ность и заботы о синхронизации для них не приоритет-ны. К тому же обычно они (MongoDB, например) при-надлежат классу NoSQL СУБД, а значит по определениюлишены всей десятилетиями накопленной мощи SQL.

С другой стороны традиционные SQL-СУБД имеют встро-енные средства текстового поиска. Оператор LIKE вхо-дит в стандартный синтаксис SQL, но гибкость его явнонедостаточна. В результате производителям СУБД прихо-дилось добавлять собственные расширения к стандартуSQL. У PostgreSQL это операторы сравнения ILIKE, ~,~*, но и они не решают всех проблем, так как не уме-ют учитывать грамматические вариации слов, не при-способлены для ранжирования и не слишком быстроработают.

Если говорить об инструментах собственно полнотек-стового поиска, то важно понимать, что до их стандар-тизации пока далеко, в каждой реализации СУБД свойсинтаксис и свои подходы. В этом контексте российскийпользователь PostgreSQL получает немалые преиму-щества: расширения полнотекстового поиска для этой

89

СУБД созданы российскими разработчиками, поэтомувозможность прямого контакта со специалистами илидаже посещение их лекций поможет углубиться в техно-логические детали, если в этом возникнет потребность.Здесь же мы ограничимся простыми примерами.

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

test=# CREATE TABLE course_chapters(c_no text REFERENCES courses(c_no),ch_no text,ch_title text,txt text,CONSTRAINT pkt_ch PRIMARY KEY(ch_no, c_no)

);

CREATE TABLE

Введем в таблицу тексты первых лекций по знакомымнам специальностям CS301 и CS305:

test=# INSERT INTO course_chapters(c_no, ch_no,ch_title, txt)

VALUES('CS301', 'I', 'Базы данных','С этой главы начинается наше знакомство ' ||'с увлекательным миром баз данных'),('CS301', 'II', 'Первые шаги','Продолжаем знакомство с миром баз данных. ' ||'Создадим нашу первую текстовую базу данных'),('CS305', 'I', 'Локальные сети','Здесь начнется наше полное приключений ' ||'путешествие в интригующий мир сетей');

INSERT 0 3

90

Проверим результат:

test=# SELECT ch_no AS no, ch_title, txtFROM course_chapters \gx

-[ RECORD 1 ]-----------------------------------------no | Ich_title | Базы данныхtxt | С этой главы начинается наше знакомство с

увлекательным миром баз данных-[ RECORD 2 ]-----------------------------------------no | IIch_title | Первые шагиtxt | Продолжаем знакомство с миром баз данных.

Создадим нашу первую текстовую базу данных-[ RECORD 3 ]-----------------------------------------no | Ich_title | Локальные сетиtxt | Здесь начнется наше полное приключений

путешествие в интригующий мир сетей

Найдем в таблице информацию по базам данных тради-ционными средствами SQL, то есть используя операторLIKE:

test=# SELECT txtFROM course_chaptersWHERE txt LIKE '%базы данных%' \gx

Мы получим предсказуемый ответ: 0 строк. Ведь LIKEне знает, что в родительном падеже следует искать «базданных» или «базу данных» в творительном. Запрос

test=# SELECT txtFROM course_chaptersWHERE txt LIKE '%базу данных%' \gx

91

выдаст строку из главы II (но не из главы I, где базав другом падеже):

-[ RECORD 1 ]-----------------------------------------txt | Продолжаем знакомство с миром баз данных.

Создадим нашу первую текстовую базу данных

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

Поэтому мы добавим к таблице глав еще один столбецсо специальным типом данных — tsvector:

test=# ALTER TABLE course_chaptersADD txtvector tsvector;

test=# UPDATE course_chaptersSET txtvector = to_tsvector('russian',txt);

test=# SELECT txtvector FROM course_chapters \gx

-[ RECORD 1 ]-----------------------------------------txtvector | 'баз':10 'глав':3 'дан':11 'знакомств':6

'мир':9 'начина':4 'наш':5 'увлекательн':8-[ RECORD 2 ]-----------------------------------------txtvector | 'баз':5,11 'дан':6,12 'знакомств':2

'мир':4 'наш':8 'перв':9 'продолжа':1'создад':7 'текстов':10

-[ RECORD 3 ]-----------------------------------------txtvector | 'интриг':8 'мир':9 'начнет':2 'наш':3

'полн':4 'приключен':5 'путешеств':6'сет':10

92

Мы видим, что в строках:

1. слова сократились до их неизменяемых частей(лексем),

2. появились цифры, означающие позицию вхожде-ния слова в текст (видно, что некоторые слова во-шли 2 раза),

3. в строку не вошли предлоги (а также не вошли бысоюзы и прочие не значимые для поиска единицыпредложения — так называемые стоп-слова).

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

test=# UPDATE course_chaptersSET txtvector =

setweight(to_tsvector('russian',ch_title),'B')|| ' ' ||setweight(to_tsvector('russian',txt),'D');

UPDATE 3

test=# SELECT txtvector FROM course_chapters \gx

-[ RECORD 1 ]-----------------------------------------txtvector | 'баз':1B,12 'глав':5 'дан':2B,13

'знакомств':8 'мир':11 'начина':6 'наш':7'увлекательн':10

-[ RECORD 2 ]-----------------------------------------txtvector | 'баз':7,13 'дан':8,14 'знакомств':4

'мир':6 'наш':10 'перв':1B,11 'продолжа':3'создад':9 'текстов':12 'шаг':2B

-[ RECORD 3 ]-----------------------------------------txtvector | 'интриг':10 'локальн':1B 'мир':11

'начнет':4 'наш':5 'полн':6 'приключен':7'путешеств':8 'сет':2B,12

93

У лексем появился относительный вес — B и D (из че-тырех возможных — A, B, C, D). Реальный вес мы будемзадавать при составлении запросов. Это придаст им до-полнительную гибкость.

Во всеоружии вернемся к поиску. Функции to_tsvector

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

test=# SELECT ch_titleFROM course_chaptersWHERE txtvector @@

to_tsquery('russian','базы & данные');

ch_title-------------Базы данныхПервые шаги(2 rows)

Можно убедиться, что 'база & данных' и другие грам-матические вариации дадут тот же результат. Мы ис-пользовали оператор сравнения @@ (две собаки), выпол-няющий работу, аналогичную LIKE. Синтаксис операторане допускает выражение естественного языка с пробе-лами, такие как «база данных», поэтому слова соединя-ются логическим оператором «и».

Аргумент russian указывает на конфигурацию, которуюиспользует СУБД. Она определяет подключаемые сло-вари и парсер, разбивающий фразу на отдельные лек-семы. Словари, несмотря на такое название, позволяютвыполнять любые преобразования лексем. Например,простой словарь-стеммер типа snowball, используемый

94

по умолчанию, оставляет от слова только неизменяемуючасть — именно поэтому поиск игнорирует окончанияслов в запросе. Можно подключать и другие словари,такие, как hunspell (позволяет более точно учитыватьморфологию слов), unaccent (превращает букву «ё»в «е») и другие.

Введенные нами веса позволяют вывести записи по ре-зультатам рейтинга:

test=# SELECT ch_title,ts_rank_cd('{0.1, 0.0, 1.0, 0.0}', txtvector, q)

FROM course_chapters,to_tsquery('russian','базы & данных') q

WHERE txtvector @@ qORDER BY ts_rank_cd DESC;

ch_title | ts_rank_cd-------------+------------Базы данных | 1.11818Первые шаги | 0.22(2 rows)

Массив {0.1, 0.0, 1.0, 0.0} задает веса. Это не обязатель-ный аргумент функции ts_rank_cd, по умолчанию мас-сив {0.1, 0.2, 0.4, 1.0} соответствует D, C, B, A. Вес словаповышает значимость найденной строки, помогает ран-жировать результаты.

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

95

test=# SELECT ts_headline('russian',txt,to_tsquery('russian', 'мир'),'StartSel=<b>, StopSel=</b>, MaxWords=50, MinWords=5'

)FROM course_chaptersWHERE to_tsvector('russian', txt) @@

to_tsquery('russian', 'мир');

-[ RECORD 1 ]-----------------------------------------ts_headline | знакомство с увлекательным <b>миром</b>

баз данных-[ RECORD 2 ]-----------------------------------------ts_headline | <b>миром</b> баз данных. Создадим нашу-[ RECORD 3 ]-----------------------------------------ts_headline | путешествие в интригующий <b>мир</b>

сетей

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

Более подробно о полнотекстовом поиске можно узнатьв документации PostgreSQL: www.postgrespro.ru/doc/textsearch.html.

Работа с JSON и JSONB

Реляционные базы данных, использующие SQL, создава-лись с большим запасом прочности: первой заботой ихпотребителей была целостность и безопасность данных,

96

а объемы информации были несравнимы с современ-ными. Когда появилось новое поколение СУБД — NoSQL,сообщество призадумалось: куда более простая струк-тура данных (вначале это были прежде всего огромныетаблицы с всего двумя колонками: ключ-значение) поз-воляла ускорить поиск на порядки. Они могли обраба-тывать небывалые объемы информации и легко мас-штабировались, вовсю используя параллельные вычис-ления. В NoSQL-базах не было необходимости хранитьинформацию по строкам, а хранение по столбцам длямногих задач позволяло еще больше ускорить и распа-раллелить вычисления.

Когда прошел первый шок, стало понятно, что для боль-шинства реальных задач простенькой структурой необойтись. Стали появляться сложные ключи, потом груп-пы ключей. Реляционные СУБД не желали отставать отжизни и начали добавлять возможности, типичные дляNoSQL.

Поскольку в реляционных СУБД изменение схемы дан-ных связано с большими вычислительными издержками,оказался как никогда кстати новый тип данных — JSON.Изначально он предназначался для JS-программистов,в том числе для AJAX-приложений, отсюда JS в назва-нии. Он как бы брал сложность добавляемых данныхна себя, позволяя создавать линейные и иерархическиеструктуры-объекты, добавление которых не требовалопересчета всей базы.

Тем, кто делал приложения, уже не было необходимостимодифицировать схему базы данных. Синтаксис JSONпохож на XML своим строгим соблюдением иерархии

97

данных. JSON достаточно гибок для того, чтобы рабо-тать с разнородной, иногда непредсказуемой структуройданных.

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

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

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

Создадим таблицу с объектами JSON:

test=# CREATE TABLE student_details(de_id int,s_id int REFERENCES students(s_id),details json,CONSTRAINT pk_d PRIMARY KEY(s_id, de_id)

);

98

test=# INSERT INTO student_details(de_id, s_id, details)

VALUES(1, 1451,'{ "достоинства": "отсутствуют",

"недостатки":"неумеренное употребление мороженого"

}'),(2, 1432,'{ "хобби":

{ "гитарист":{ "группа": "Постгрессоры",

"гитары":["страт","телек"]}

}}'),(3, 1556,'{ "хобби": "косплей",

"достоинства":{ "мать-героиня":

{ "Вася": "м","Семен": "м","Люся": "ж","Макар": "м","Саша":"сведения отсутствуют"

}}

}'),(4, 1451,'{ "статус": "отчислена"}');

Проверим, все ли данные на месте. Для удобства соеди-ним таблицы student_details и students при помощиконструкции WHERE, ведь в новой таблице отсутствуютимена студентов:

99

test=# SELECT s.name, sd.detailsFROM student_details sd, students sWHERE s.s_id = sd.s_id \gx-[ RECORD 1 ]--------------------------------------name | Аннаdetails | { "достоинства": "отсутствуют", +

| "недостатки": +| "неумеренное употребление мороженого" +| }

-[ RECORD 2 ]--------------------------------------name | Викторdetails | { "хобби": +

| { "гитарист": +| { "группа": "Постгрессоры", +| "гитары":["страт","телек"] +| } +| } +| }

-[ RECORD 3 ]--------------------------------------name | Нинаdetails | { "хобби": "косплей", +

| "достоинства": +| { "мать-героиня": +| { "Вася": "м", +| "Семен": "м", +| "Люся": "ж", +| "Макар": "м", +| "Саша":"сведения отсутствуют" +| } +| } +| }

-[ RECORD 4 ]--------------------------------------name | Аннаdetails | { "статус": "отчислена" +

| }

Допустим, нас интересуют записи, где есть информацияо достоинствах студентов. Мы можем обратиться к со-держанию ключа «достоинство», используя специальныйоператор ->>:

100

test=# SELECT s.name, sd.detailsFROM student_details sd, students sWHERE s.s_id = sd.s_idAND sd.details ->> 'достоинства' IS NOT NULL \gx

-[ RECORD 1 ]--------------------------------------name | Аннаdetails | { "достоинства": "отсутствуют", +

| "недостатки": +| "неумеренное употребление мороженого" +| }

-[ RECORD 2 ]--------------------------------------name | Нинаdetails | { "хобби": "косплей", +

| "достоинства": +| { "мать-героиня": +| { "Вася": "м", +| "Семен": "м", +| "Люся": "ж", +| "Макар": "м", +| "Саша":"сведения отсутствуют" +| } +| } +| }

Мы убедились, что две записи имеют отношение к до-стоинствам Анны и Нины, однако такой ответ нас врядли удовлетворит, так как на самом деле достоинства Ан-ны «отсутствуют». Скорректируем запрос:

test=# SELECT s.name, sd.detailsFROM student_details sd, students sWHERE s.s_id = sd.s_idAND sd.details ->> 'достоинства' IS NOT NULLAND sd.details ->> 'достоинства' != 'отсутствуют';

Убедитесь, что этот запрос оставит в списке только Нину,обладающую реальными, а не отсутствующими достоин-ствами.

101

Но такой способ срабатывает не всегда. Попробуем най-ти, на каких гитарах играет музыкант Витя:

test=# SELECT sd.de_id, s.name, sd.detailsFROM student_details sd, students sWHERE s.s_id = sd.s_idAND sd.details ->> 'гитары' IS NOT NULL \gx

Запрос ничего не выдаст. Дело в том, что соответству-ющая пара ключ-значение находится внутри иерархииJSON, вложена в пары более высокого уровня:

name | Викторdetails | { "хобби": +

| { "гитарист": +| { "группа": "Постгрессоры", +| "гитары":["страт","телек"] +| } +| } +| }

Чтобы добраться до гитар, воспользуемся оператором#> и спустимся с «хобби» вниз по иерархии:

test=# SELECT sd.de_id, s.name,sd.details #> 'хобби,гитарист,гитары'

FROM student_details sd, students sWHERE s.s_id = sd.s_idAND sd.details #> 'хобби,гитарист,гитары'

IS NOT NULL \gx

и убедимся, что Виктор фанат фирмы Fender:

de_id | name | ?column?-------+--------+-------------------

2 | Виктор | ["страт","телек"]

102

У типа данных json есть младший брат jsonb. Буква «b»подразумевает бинарный (а не текстовый) способ хра-нения данных. Такие данные можно плотно упаковать ипоиск по ним работает быстрее. Последнее время jsonb

используется намного чаще, чем json.

test=# ALTER TABLE student_detailsADD details_b jsonb;

test=# UPDATE student_detailsSET details_b = to_jsonb(details);

test=# SELECT de_id, details_bFROM student_details \gx

-[ RECORD 1 ]--------------------------------------de_id | 1details_b | {"недостатки": "неумеренное

употребление мороженого","достоинства": "отсутствуют"}

-[ RECORD 2 ]--------------------------------------de_id | 2details_b | {"хобби": {"гитарист": {"гитары":

["страт", "телек"], "группа":"Постгрессоры"}}}

-[ RECORD 3 ]--------------------------------------de_id | 3details_b | {"хобби": "косплей", "достоинства":

{"мать-героиня": {"Вася": "м", "Люся":"ж", "Саша": "сведения отсутствуют","Макар": "м", "Семен": "м"}}}

-[ RECORD 4 ]--------------------------------------de_id | 4details_b | {"статус": "отчислена"}

Можно заметить, что, кроме иной формы записи, изме-нился порядок значений в парах: Саша, сведения о ко-торой, как мы помним, отсутствуют, заняла теперь место

103

в списке перед Макаром. Это не недостаток jsonb отно-сительно json, а особенность хранения информации.

Для работы с jsonb набор операторов больше. Один изполезнейших операторов — оператор вхождения в объ-ект @>. Он напоминает #> для json.

Найдем запись, где упоминается дочь матери-героиниЛюся:

test=# SELECT s.name,jsonb_pretty(sd.details_b) json

FROM student_details sd, students sWHERE s.s_id = sd.s_idAND sd.details_b @>

'{"достоинства":{"мать-героиня":{}}}' \gx

-[ RECORD 1 ]-------------------------------------name | Нинаjson | { +

| "хобби": "косплей", +| "достоинства": { +| "мать-героиня": { +| "Вася": "м", +| "Люся": "ж", +| "Саша": "сведения отсутствуют",+| "Макар": "м", +| "Семен": "м" +| } +| } +| }

Мы использовали функцию jsonb_pretty(), котораяформатирует вывод типа jsonb.

Или можно воспользоваться функцией jsonb_each(),разворачивающей пары ключ-значение:

104

test=# SELECT s.name,jsonb_each(sd.details_b)

FROM student_details sd, students sWHERE s.s_id = sd.s_idAND sd.details_b @>

'{"достоинства":{"мать-героиня":{}}}' \gx

-[ RECORD 1 ]-------------------------------------name | Нинаjsonb_each | (хобби,"""косплей""")-[ RECORD 2 ]-------------------------------------name | Нинаjsonb_each | (достоинства,"{""мать-героиня"":

{""Вася"": ""м"", ""Люся"": ""ж"",""Саша"": ""сведения отсутствуют"",""Макар"": ""м"", ""Семен"":""м""}}")

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

Но главное, пожалуй, возможность создавать для jsonb

индексы, поддерживающие оператор @>, обратный ему<@ и многие другие. Среди имеющихся для jsonb индек-сов, как правило, лучше всего подходит GIN. Для json

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

Подробнее о типах json и jsonb и о функциях для ра-боты с ними можно узнать на страницах документа-ции PostgreSQL postgrespro.ru/doc/datatype-json

и postgrespro.ru/doc/functions-json.

105

PostgreSQLдля приложения

Отдельный пользователь

В предыдущей главе мы подключались к серверу базданных под пользователем postgres, единственным су-ществующим сразу после установки СУБД. Но postgres

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

postgres=# CREATE USER app PASSWORD 'p@ssw0rd';

CREATE ROLE

postgres=# CREATE DATABASE appdb OWNER app;

CREATE DATABASE

Подробнее про пользователей и привилегии:postgrespro.ru/doc/user-manag.html

и postgrespro.ru/doc/ddl-priv.html.

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

106

postgres=# \c appdb app localhost 5432

Password for user app: ***You are now connected to database "appdb" as user"app" on host "127.0.0.1" at port "5432".

appdb=>

В команде указываются последовательно имя базы дан-ных (appdb), имя пользователя (app), узел (localhost или127.0.0.1) и номер порта (5432). Обратите внимание, чтоподсказка изменилась: вместо «решетки» (#) теперьотображается символ «больше» (>) – решетка указы-вает на суперпользователя по аналогии с пользователемroot в Unix.

Со своей базой данных пользователь app может рабо-тать без ограничений. Например, можно создать в нейтаблицу:

appdb=> CREATE TABLE greeting(s text);

CREATE TABLE

appdb=> INSERT INTO greeting VALUES ('Привет, мир!');

INSERT 0 1

Удаленное подключение

В нашем примере клиент и СУБД находятся на одноми том же компьютере. Разумеется, вы можете устано-вить PostgreSQL на выделенный сервер, а подключатьсяк нему с другой машины (например, с сервера приложе-ний). В этом случае вместо localhost надо указать адрес

107

вашего сервера СУБД. Но этого недостаточно: по умол-чанию из соображений безопасности PostgreSQL допус-кает только локальные подключения.

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

Во-первых, файл основных настроек postgresql.conf

(обычно располагается в каталоге баз данных). Найди-те строку, определяющую, какие сетевые интерфейсыслушает PostgreSQL:

#listen_addresses = 'localhost'

и замените ее на:

listen_addresses = '*'

Во-вторых, ph_hba.conf — файл с настройками аутен-тификации. Когда клиент пытается подключиться к сер-веру, PostgreSQL находит в этом файле первую сверхустроку, соответствующую параметрам соединения по че-тырем параметрам: тип соединения, имя базы данных,имя пользователя и IP-адрес клиента. В той же строкенаписано, как пользователь должен подтвердить, что ондействительно тот, за кого себя выдает.

Например, в Debian и Ubuntu в этом файле есть, в числепрочих, такая строка:

local all all peer

108

Она говорит о том, что локальные соединения (local)к любой базе данных (all) под любым пользователем(all) должны проверяться методом peer (IP-адрес длялокальных соединений, конечно, не указывается).

Метод peer означает, что PostgreSQL запрашивает имятекущего пользователя у операционной системы и счи-тает, что ОС уже выполнила необходимую проверку(спросила у пользователя пароль). Поэтому в Linux-подобных операционных системах пользователю обыч-но не приходится вводить пароль при подключениик серверу на своем компьютере: достаточно того, чтопароль был введен при входе в систему.

А вот Windows не поддерживает локальных соединений,и там строка выглядит следующим образом:

host all all 127.0.0.1/32 md5

Она означает, что сетевые соединения (host) к любойбазе данных (all) под любым пользователем (all) с ло-кального адреса (127.0.0.1) должны проверяться мето-дом md5. Этот метод подразумевает ввод пользователемпароля.

Итак, для наших целей допишите в конец pg_hba.conf

следующую строку:

host appdb app all md5

Это разрешит доступ к базе данных appdb пользователюapp с любого адреса при указании верного пароля.

109

После внесения изменений в конфигурационные файлыне забудьте попросить сервер перечитать настройки:

postgres=# SELECT pg_reload_conf();

Подробнее о настройках аутентификации:postgrespro.ru/doc/client-authentication.html

Проверка связи

Для того, чтобы обратиться к PostgreSQL из програм-мы на каком-либо языке программирования, необходи-мо использовать подходящую библиотеку и установитьдрайвер СУБД.

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

Если вы используете ОС Windows, не забудьте в окнеCommand Prompt сменить шрифт на TrueType (например,«Lucida Console» или «Consolas») и выполнить команды:

C:\> chcp 1251

Active code page: 1251

C:\> set PGCLIENTENCODING=WIN1251

110

PHP

В языке PHP работа с PostgreSQL организована с помо-щью специального расширения. В Linux кроме самогоPHP нам потребуется пакет с этим расширением:

$ sudo apt-get install php5-cli php5-pgsql

PHP для Windows можно установить с сайта windows.

php.net/download. Расширение для PostgreSQL уже вхо-дит в комплект, но в файле php.ini необходимо найтии раскомментировать строку (убрать точку с запятой):

;extension=php_pgsql.dll

Пример программы (test.php):

<?php$conn = pg_connect('host=localhost port=5432 ' .

'dbname=appdb user=app ' .'password=p@ssw0rd') or die;

$query = pg_query('SELECT * FROM greeting') or die;while ($row = pg_fetch_array($query)) {

echo $row[0].PHP_EOL;}pg_free_result($query);pg_close($conn);

?>

Выполняем:

$ php test.phpПривет, мир!

Расширение для PostgreSQL описано в документации:php.net/manual/ru/book.pgsql.php.

111

Perl

В языке Perl работа с базами данных организованас помощью интерфейса DBI. Сам Perl предустановленв Debian и Ubuntu, так что дополнительно нужен толькодрайвер:

$ sudo apt-get install libdbd-pg-perl

Существует несколько сборок Perl для Windows, кото-рые перечислены на сайте www.perl.org/get.html.ActiveState Perl и Strawberry Perl уже включают необ-ходимый для PostgreSQL драйвер.

Пример программы (test.pl):

use DBI;my $conn = DBI->connect(

'dbi:Pg:dbname=appdb;host=localhost;port=5432','app','p@ssw0rd') or die;

my $query = $conn->prepare('SELECT * FROM greeting');$query->execute() or die;while (my @row = $query->fetchrow_array()) {

print @row[0]."\n";}$query->finish();$conn->disconnect();

Выполняем:

$ perl test.pl

Привет, мир!

Интерфейс описан в документации:metacpan.org/pod/DBD::Pg.

112

Python

В языке Python для работы с PostgreSQL обычно ис-пользуется библиотека psycopg (название произносит-ся как «сайко-пи-джи»). В Debian и Ubuntu язык Pythonверсии 2 предустановлен, так что нужен только драйвер:

$ sudo apt-get install python-psycopg2

(Если вы используете Python 3, установите вместо этогопакет python3-psycopg2.)

Python для операционной системы Windows можновзять с сайта www.python.org. Библиотека psycopg до-ступна на сайте проекта initd.org/psycopg (выбери-те версию, соответствующую установленной версииPython). Там же находится необходимая документация.

Пример программы (test.py):

import psycopg2conn = psycopg2.connect(

host='localhost', port='5432', database='appdb',user='app', password='p@ssw0rd')

cur = conn.cursor()cur.execute('SELECT * FROM greeting')rows = cur.fetchall()for row in rows:

print row[0]conn.close()

Выполняем:

$ python test.py

Привет, мир!

113

Java

В языке Java работа с базой данных организована черезинтерфейс JDBC. Устанавливаем JDK 1.7; дополнительнонам потребуется пакет с драйвером JDBC:

$ sudo apt-get install openjdk-7-jdk

$ sudo apt-get install libpostgresql-jdbc-java

JDK для Windows можно скачать с www.oracle.com/technetwork/java/javase/downloads. Драйвер JDBCдоступен на сайте jdbc.postgresql.org (выберите вер-сию, соответствующую установленной версии JDK), тамже находится и документация.

Пример программы (Test.java):

import java.sql.*;public class Test {

public static void main(String[] args)throws SQLException {

Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/appdb","app", "p@ssw0rd");

Statement st = conn.createStatement();ResultSet rs = st.executeQuery(

"SELECT * FROM greeting");while (rs.next()) {

System.out.println(rs.getString(1));}rs.close();st.close();conn.close();

}}

114

Компилируем и выполняем, указывая путь к классу-драйверу JDBC (в Windows пути разделяются не двое-точием, а точкой с запятой):

$ javac Test.java

$ java -cp .:/usr/share/java/postgresql-jdbc4.jar \Test

Привет, мир!

Резервное копирование

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

$ pg_dump appdb > appdb.dump

Если вы посмотрите получившийся файл appdb.dump

с помощью текстового редактора, то обнаружите в немобычные команды SQL, создающие и заполняющие дан-ными все объекты appdb. Этот файл можно подать навход psql, чтобы восстановить содержимое базы дан-ных. Например, можно создать новую БД и загрузитьданные туда:

$ createdb appdb2

$ psql -d appdb2 -f appdb.dump

115

У pg_dump много возможностей, с которыми стоит по-знакомиться: postgrespro.ru/doc/app-pgdump. Некото-рые из них доступны, только когда данные выгружаютсяв специальном внутреннем формате; в таком случае длявосстановления нужно использовать не psql, а утилитуpg_restore.

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

Для больших серьезных проектов требуется продуман-ная стратегия периодического резервного копирова-ния. Для этого лучше подойдет физическая «двоич-ная» копия кластера, которую можно получить утили-той pg_basebackup. Подробнее про доступные средстварезервного копирования и восстановления смотритев документации: postgrespro.ru/doc/backup.

Штатные средства PostgreSQL позволяют сделать прак-тически все, что нужно, но требуют выполнения мно-гочисленных шагов, нуждающихся в автоматизации.Поэтому многие компании создают собственные ин-струменты резервирования для упрощения этой зада-чи. Такой инструмент есть и у нашей компании PostgresProfessional. Он называется pg_probackup, распростра-няется свободно и позволяет выполнять инкременталь-ное резервное копирование на уровне страниц, кон-тролировать целостность данных, работать с большимиобъемами информации за счет параллелизма и сжа-тия, реализовывать различные стратегии резервного

116

копирования. Полная документация доступна по адресуpostgrespro.ru/doc/app-pgprobackup.

Что дальше?

Теперь вы готовы к разработке вашего приложения.По отношению к базе данных оно всегда будет состо-ять из двух частей: серверной и клиентской. Сервернаячасть — это все, что относится к СУБД: таблицы, индек-сы, представления, триггеры, хранимые функции. А кли-ентская — все, что работает вне СУБД и подключаетсяк ней; с точки зрения базы данных не важно, будет лиэто «толстый» клиент или сервер приложений.

Важный вопрос, на который нет однозначного правиль-ного ответа: где должна быть сосредоточена бизнес-логика приложения?

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

117

Иногда такое решение мотивируют желанием обеспе-чить переносимость приложения на любую СУБД.

Подход имеет право на существование; если система,построенная таким образом, работает и выполняет воз-лагаемые на нее задачи — почему бы нет?

Но у этого решения есть и очевидные недостатки:

• Поддержка согласованности данных возлагается наприложение.Вместо того, чтобы поручить СУБД следить за согла-сованностью данных (а это именно то, чем сильныреляционные системы), приложение самостоятельновыполняет необходимые проверки. Будьте увере-ны, что рано или поздно в базу попадут некоррект-ные данные. Эти ошибки придется исправлять илиучить приложение работать с ними. А ведь бываютситуации, когда над одной базой данных строятсянесколько разных приложений: в этом случае обой-тись без помощи СУБД просто невозможно.

• Производительность оставляет желать лучшего.ORM-системы позволяют в какой-то мере абстраги-роваться от СУБД, но качество генерируемых имиSQL-запросов весьма сомнительно. Как правило, вы-полняется много небольших запросов, каждый изкоторых сам по себе работает достаточно быстро.Но такая схема выдерживает только небольшие на-грузки на небольшом объеме данных, и практическине поддается какой-либо оптимизации со стороныСУБД.

118

• Усложняется прикладной код.На прикладном языке программирования невозмож-но сформулировать по-настоящему сложный запрос,который бы автоматически и адекватно трансли-ровался в SQL. Поэтому сложную обработку (еслиона нужна, разумеется) приходится программиро-вать на уровне приложения, предварительно выби-рая из базы все необходимые данные. При этом,во-первых, выполняется лишняя пересылка данныхпо сети, а во-вторых, алгоритмы работы с данными(сканирования, соединения, сортировки, агрегации)в СУБД отлаживаются и оптимизируются десятилетия-ми и справятся в задачей гарантированно лучше, чемприкладной код.

Конечно, использование СУБД на полную мощность,с реализацией всех ограничений целостности и логи-ки работы с данными в хранимых функциях, требуетвдумчивого изучения ее особенностей и возможностей.Потребуется освоить язык SQL для написания запро-сов и какой-либо язык серверного программирования(обычно PL/pgSQL) для написания функций и триггеров.Взамен вы овладеете надежным инструментом, однимиз важных «кубиков» в архитектуре любой информаци-онной системы.

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

119

pgAdmin

pgAdmin — популярное графическое средство для адми-нистрирования PostgreSQL. Программа упрощает основ-ные задачи администрирования, отображает объектыбаз данных, позволяет выполнять запросы SQL.

Долгое время стандартом де-факто являлся pgAdmin 3,однако разработчики из EnterpriseDB прекратили егоподдержку и в 2016 году выпустили новую, четвертую,версию, полностью переписав продукт с языка C++ наPython и веб-технологии. Из-за изменившегося интер-фейса pgAdmin 4 поначалу был встречен достаточнопрохладно, но продолжает разрабатываться и совер-шенствоваться.

Тем не менее, третья версия еще не окончательно за-быта, знамя разработки подхватила команда BigSQL:www.openscg.com/bigsql/pgadmin3.

Мы же посмотрим на основные возможности новогоpgAdmin 4.

120

Установка

Чтобы запустить pgAdmin 4 на Windows, воспользуйтесьустановщиком на странице www.pgadmin.org/download/.Процесс установки прост и очевиден, все предлагаемыезначения можно оставить без изменений.

Для систем Debian и Ubuntu, к сожалению, пока нет го-тового пакета, поэтому опишем процесс подробно. Сна-чала устанавливаем пакеты для языка Python:

$ sudo apt-get install virtualenv python-pip \libpq-dev python-dev

Затем инициализируем виртуальную среду в каталогеpgadmin4 (конечно, можно выбрать и другой каталог):

$ cd ~$ virtualenv pgadmin4$ cd pgadmin4$ source bin/activate

Теперь устанавливаем собственно программу pgAdmin.Последнюю актуальную версию можно посмотреть здесь:www.pgadmin.org/download/pgadmin-4-python-wheel/.

$ pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v2.0/pip/pgadmin4-2.0-py2.py3-none-any.whl

$ rm -rf ~/.pgadmin/

Последнее, что осталось сделать — сконфигурироватьpgAdmin для работы в автономном режиме (другой, сер-верный, режим мы не будем здесь рассматривать).

121

$ cat <<EOF \>lib/python2.7/site-packages/pgadmin4/config_local.pyimport osDATA_DIR = os.path.realpath(

os.path.expanduser(u'~/.pgadmin/'))LOG_FILE = os.path.join(DATA_DIR, 'pgadmin4.log')SQLITE_PATH = os.path.join(DATA_DIR, 'pgadmin4.db')SESSION_DB_PATH = os.path.join(DATA_DIR, 'sessions')STORAGE_DIR = os.path.join(DATA_DIR, 'storage')SERVER_MODE = FalseEOF

К счастью, эти действия надо проделать один раз.

Для запуска pgAdmin 4 выполните:

$ cd ~/pgadmin4$ source bin/activate$ python \

lib/python2.7/site-packages/pgadmin4/pgAdmin4.py

Теперь пользовательский интерфейс (полностью пере-веденный на русский язык нашей компанией) доступенвам в веб-браузере по адресу localhost:5050.

Возможности

Подключение к серверу

В первую очередь настроим подключение к серверу.Нажмите на значок Добавить новый сервер (Add NewServer) и в появившемся окне на вкладке Общие (General)введите произвольное имя (Name) для соединения, а на

122

вкладке Соединение (Connection) — имя сервера (Hostname/address), порт (Port), имя пользователя (Username)и пароль (Password). Если не хотите вводить пароль каж-дый раз вручную, отметьте флажок Сохранить пароль(Save password).

При нажатии на кнопку Сохранить (Save) программапроверит доступность сервера с указанными парамет-рами и запомнит новое подключение.

123

Навигатор

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

• appdb мы создали для проверки подключенияк PostgreSQL из разных языков программирования,

• demo — демонстрационная база данных,

• postgres создается автоматически при установкеСУБД,

• test мы использовали, когда знакомились с SQL.

124

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

Для каждого типа объекта в контекстном меню (по пра-вой кнопке мыши) приведен список действий, которыес ним можно совершить. Например, выгрузить в файлили загрузить из файла, выдать привилегии, удалить.

В правой части окна на отдельных вкладках выводитсясправочная информация:

• Панель информации (Dashboard) — показывает гра-фики, отражающие активность системы;

• Свойства (Properties) — свойства выбранного объекта(например, для столбца будет показан тип его дан-ных и т. п.);

• SQL — команда SQL для создания выбранного в на-вигаторе объекта;

• Статистика (Statistics) — информация, которая ис-пользуется оптимизатором для построения плановвыполнения запросов и может рассматриваться ад-министратором СУБД для анализа ситуации;

• Зависимости и Зависимые (Dependencies, Dependents) —зависимости между выбранным объектом и другимиобъектами в базе данных.

125

Выполнение запросов

Чтобы выполнить запрос, откройте новую вкладку с ок-ном SQL, выбрав в меню Инструменты — Запросник(Tools — Query tool).

Введите запрос в верхней части окна и нажмите F5.В нижней части окна на вкладке Результат (Data Output)появится результат запроса.

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

126

в истории команд на вкладке История запросов (QueryHistory).

Другое

Программа pgAdmin предоставляет графический ин-терфейс для стандартных утилит PostgreSQL, таблицсистемного каталога, административных функций и ко-манд SQL. Особо отметим встроенный отладчик PL/pgSQL-кода. Со всеми возможностями этой программы вы мо-жете познакомиться на сайте продукта www.pgadmin.org

или в справочной системе самой программы.

127

Обучение и документация

Документация

Для серьезной работы с PostgreSQL не обойтись безчтения документации. Это не только описание всех воз-можностей СУБД, но и исчерпывающее справочное ру-ководство, которое всегда должно быть под рукой. Чи-тая документацию, вы получаете емкую и точную ин-формацию из первых рук — она написана самими раз-работчиками и всегда аккуратно поддерживается в акту-альном состоянии.

Мы в Postgres Professional перевели на русский языквесь комплект документации — он доступен на нашемсайте: www.postgrespro.ru/docs.

Глоссарий, составленный нами для перевода, опубли-кован по адресу postgrespro.ru/education/glossary.Мы рекомендуем использовать его, чтобы грамотно пе-реводить англоязычные документы и использовать еди-ную, понятную всем терминологию для материалов нарусском языке.

Предпочитающие оригинальную документацию на ан-глийском языке найдут ее и на нашем сайте, и по адре-су www.postgresql.org/docs.

128

Учебные курсы

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

• DBA1. Администрирование PostgreSQL. Базовый курс.

• DBA2. Администрирование PostgreSQL. Расширенныйкурс.

• DEV1. Базовый курс для разработчиков сервернойчасти приложения.

• DEV2. Расширенный курс для разработчиков сервер-ной части приложения.

Деление курсов на базовые и расширенные вызванобольшим объемом информации, который невозможноизложить и усвоить за несколько дней. Не стоит считать,что базовый курс предназначен только для новичков,а расширенный — только для опытных администраторовили разработчиков. Хотя между курсами есть пересече-ния по темам, но их не очень много.

Например, базовый трехдневный курс DBA1 знакомитс PostgreSQL и подробно объясняет базовые понятияадминистрирования, а пятидневный DBA2 охватыва-ет подробности внутреннего устройства СУБД и ее на-стройки, оптимизацию запросов и ряд других тем. Рас-ширенный курс предполагает, что к темам базового кур-са возвращаться не нужно. Те же принципы лежат в ос-нове деления на две части курса для разработчиков.

129

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

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

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

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

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

130

Курсы для администраторов

DBA1. Администрирование PostgreSQL.Базовый курс

Продолжительность: 3 дня

Предварительные знания:

Минимальные сведения о базах данных и SQL.Знакомство с Unix.

Какие навыки будут получены:

Общее сведения об архитектуре PostgreSQL.Установка, базовая настройка, управление сервером.Организация данных на логическом и физическомуровнях.Базовые задачи администрирования.Управление пользователями и доступом.Представление о резервном копировании и реплика-ции.

Темы:

Базовый инструментарий

1. Установка и управление сервером2. Использование psql3. Конфигурирование

131

Архитектура

4. Общее устройство PostgreSQL5. Изоляция и многоверсионность6. Буферный кэш и журнал

Организация данных

7. Базы данных и схемы8. Системный каталог9. Табличные пространства10. Низкий уровень

Задачи администрирования

11. Мониторинг12. Сопровождение

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

13. Роли и атрибуты14. Привилегии15. Политики защиты строк16. Подключение и аутентификация

Резервное копирование

17. Обзор

Репликация

18. Обзор

Материалы учебного курса DBA1 (презентации, демон-страции, практические задания, видеозапись лекций)доступны для самостоятельного изучения по адресуwww.postgrespro.ru/education/courses/DBA1.

132

DBA2. Администрирование PostgreSQL.Расширенный курс

Продолжительность: 5 дней

Предварительные знания:

Владение Unix.Базовые знания об архитектуре, установке, настрой-ке, обслуживании СУБД.

Какие навыки будут получены:

Понимание архитектуры PostgreSQL.Мониторинг и настройка базы, решение задач опти-мизации производительности.Выполнение задач сопровождения.Резервирование и репликация.

Темы:

Введение

1. Архитектура PostgreSQL

Изоляция и многоверсионность

2. Изоляция транзакций3. Страницы и версии строк4. Снимки и блокировки5. Очистка6. Автоочистка и заморозка

133

Журналирование

7. Буферный кэш8. Упреждающий журнал9. Контрольная точка

Репликация

10. Файловая репликация11. Потоковая репликация12. Переключение на реплику13. Репликация: варианты

Основы оптимизации

14. Обработка запроса15. Методы доступа16. Способы соединения17. Статистика18. Использование памяти19. Профилирование20. Оптимизация запросов

Разные темы

21. Секционирование22. Локализация23. Обновление сервера24. Управление расширениями25. Внешние данные

Материалы учебного курса DBA2 (презентации, демон-страции, практические задания, видеозапись лекций)доступны для самостоятельного изучения по адресу:www.postgrespro.ru/education/courses/DBA2.

134

Курсы для прикладных разработчиков

DEV1. Разработка серверной части приложенийPostgreSQL. Базовый курс

Продолжительность: 4 дня

Предварительные знания:

Основы SQL.Опыт работы с каким-нибудь процедурным языкомпрограммирования.Минимальные сведения о работе в Unix.

Какие навыки будут получены:

Общие сведения об архитектуре PostgreSQL.Использование основных объектов БД:таблиц, индексов, представлений.Программирование на стороне сервера на языкахSQL и PL/pgSQL.Использование основных типов данных,включая записи и массивы.Организация взаимодействия с клиентской частьюприложения.

Темы:

Базовый инструментарий

1. Установка и управление, psql

135

Архитектура

2. Общее устройство PostgreSQL3. Изоляция и многоверсионность4. Буферный кэш и журнал

Организация данных

5. Логическая структура6. Физическая структура

Приложение «Книжный магазин»

7. Схема данных приложения8. Взаимодействие клиента с СУБД

SQL

9. Функции10. Составные типы

PL/pgSQL

11. Обзор и конструкции языка12. Выполнение запросов13. Курсоры14. Динамические команды15. Массивы16. Обработка ошибок17. Триггеры18. Отладка

Разграничение доступа

19. Обзор

Материалы учебного курса DEV1 (презентации, демон-страции, практические задания, видеозапись лекций)доступны для самостоятельного изучения по адресу:www.postgrespro.ru/education/courses/DEV1.

136

DEV2. Разработка серверной части приложенийPostgreSQL. Расширенный курс

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

Где пройти обучение

Если вы хотите пройти обучение по перечисленным кур-сам в специализированном учебном центре, под ру-ководством опытного преподавателя и с получениемсертификата, то есть и такая возможность. Мы автори-зовали несколько известных учебных центров, которыечитают наши курсы. Их список можно посмотреть здесь:www.postgrespro.ru/education/where.

Курсы для разработчиков СУБД

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

Hacking PostgreSQL

Курс «Hacking PostgreSQL» собран из личного опытаразработчиков, материалов конференций, статей и вдум-

137

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

Предварительные знания:

Знания основ языка SQL, функционала транзакций,индексов и т. п.Знание языка С в объеме, достаточном как минимумдля чтения исходных кодов (лучше иметь практиче-ские навыки).Знакомство с базовыми структурами и алгоритмами.

Темы:

1. Обзор архитектуры2. Сообщество PostgreSQL и инструменты разработчика3. Расширяемость4. Обзор исходного кода5. Физическое представление данных6. Разделяемая память и блокировки7. Локальная память процессов8. Базовое устройство планировщика и исполнителязапросов

Материалы курса Hacking PostgreSQL доступны для са-мостоятельного изучения по адресу www.postgrespro.ru/education/courses/hacking.

138

Путеводительпо галактике

Новости и обсуждения

Если вы собираетесь работать с PostgreSQL, вам захо-чется быть в курсе событий, узнавать о новых возмож-ностях предстоящего выпуска, знакомиться с другиминовостями. Много людей ведут свои блоги, публикуя ин-тересные и полезные материалы. Удобный способ полу-чить все англоязычные заметки в одном месте — читатьсайт planet.postgresql.org.

Не забывайте и про wiki.postgresql.org — сборникстатей, поддерживаемый и развиваемый сообществом.Здесь вы найдете ответы на часто задаваемые вопро-сы, обучающие материалы, статьи про настройку и оп-тимизацию, про особенности миграции с разных СУБДи многое другое. Часть материалов этого сайта доступ-на и на русском языке: wiki.postgresql.org/wiki/Russian. Вы тоже можете помощь сообществу, переведязаинтересовавшую вас англоязычную статью.

Более двух тысяч русскоязычных пользователей Post-greSQL входят в группу «PostgreSQL в России» на фейс-

139

буке: www.facebook.com/groups/postgresql.

Свой вопрос можно задать и на профильных сайтах. На-пример, на stackoverflow.com на английском языкеили ru.stackoverflow.com на русском (не забудьте по-ставить метку «postgresql»), или на форуме www.sql.ru/

forum/postgresql.

Новости нашей компании вы найдете по адресуpostgrespro.ru/blog.

Списки рассылки

Если вы хотите узнавать обо всем первым, не дожида-ясь, пока кто-нибудь напишет заметку в блоге, читайтесписки рассылки. Разработчики PostgreSQL по старойтрадиции обсуждают между собой все вопросы исклю-чительно по электронной почте. Для этого используетсясписок рассылки pgsql-hackers (часто называемый про-сто «hackers»).

Полный перечень всех списков рассылки находится поадресу www.postgresql.org/list. Среди них:

• pgsql-general для обсуждения общих вопросов,

• pgsql-bugs для сообщений о найденных ошибках,

• pgsql-announce для новостей о выходе новых версийпродуктов

и многие другие.

140

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

Другой вариант — время от времени читать архив сооб-щений на www.postgresql.org/list, или, в несколькоболее удобном виде, на www.postgresql-archive.org.

Commitfest

Еще один способ быть в курсе событий, не тратя на этомного времени — заглядывать на сайт commitfest.postgresql.org. В этой системе периодически откры-ваются «окна», в которых разработчики должны ре-гистрировать свои патчи. Например, окно 01.03.2017–31.03.2017 относилось к версии 10, а следующее за нимокно 01.09.2017–30.09.2017 — уже к следующей. Это де-лается для того, чтобы примерно за полгода до выходановой версии PostgreSQL прекратить прием новых воз-можностей и успеть стабилизировать код.

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

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

141

Конференции

В России ежегодно проводятся две крупные междуна-родные конференции, собирающие сотни разработчикови пользователей PostgreSQL:

5–7 февраля 2018 — PGConf в Москве (pgconf.ru)

июль 2018 — PGDay в Санкт-Петербурге (pgday.ru)

Конференции по PostgreSQL проводятся и во всем мире:

май — PGCon в Оттаве (pgcon.org)

ноябрь — PGConf Europe (pgconf.eu)

Кроме того, в разных городах России проводятся кон-ференции с более широкой тематикой, на которых пред-ставлено и направление баз данных и, в том числе,PostgreSQL:

март — CodeFest в Новосибирске (codefest.ru)

апрель — Dump в Екатеринбурге (dump-conf.ru)

апрель — SECON в Пензе (www.secon.ru)

апрель — Стачка в Ульяновске (nastachku.ru)

декабрь — HappyDev в Омске (happydev.ru)

Помимо конференций проходят и неофициальные регу-лярные встречи, в том числе онлайн: www.meetup.com/postgresqlrussia.

142

О компании

Компания Postgres Professional была основана в 2015году и объединила всех ключевых российских разра-ботчиков, вклад которых в развитие PostgreSQL суще-ственен и признан мировым сообществом. Компанияявляется российским вендором PostgreSQL и выполняетразработки на уровне ядра СУБД и расширений, оказы-вает услуги по проектированию и поддержке приклад-ных систем, миграции на PostgreSQL.

Компания уделяет большое внимание образовательнойдеятельности, организует крупнейшую международнуюконференцию PgConf.Russia в Москве и принимает уча-стие в конференциях по всему миру.

Наш адрес:

117036, г. Москва, ул. Дмитрия Ульянова, д. 7А

Телефон:

+7 495 150-06-91

Сайт компании и электронная почта:

postgrespro.ru

[email protected]

143

Услуги

Промышленные решения на основе PostgreSQL

• Проектирование и участие в создании критичныхвысоконагруженных систем с использованием СУБДPostgreSQL.

• Оптимизация конфигурации СУБД.

• Консультирование по вопросам использовании СУБДв промышленных системах.

• Аудит систем заказчика по вопросам использованияСУБД, проектирования баз данных, высокопроизводи-тельных и отказоустойчивых архитектур.

• Внедрение СУБД PostgreSQL.

Вендорская техническая поддержка

• Вторая и третья линии техподдержки СУБД PostgreSQLв режиме 24х7.

• Круглосуточная поддержка опытными администрато-рами: мониторинг, восстановление работоспособно-сти, анализ непредвиденных обстоятельств, обеспе-чение производительности.

• Исправление ошибок, обнаруженных в СУБД и еерасширениях.

144

Миграция прикладных систем

• Анализ имеющихся прикладных систем и определе-ние сложности их миграции с других СУБД на Post-greSQL.

• Определение архитектуры нового решения и требо-ваний к доработкам прикладных систем.

• Миграция систем на СУБД PostgreSQL, в том числедействующих систем под нагрузкой.

• Поддержка прикладных разработчиков в процессемиграции.

Разработка на уровне ядра и расширений

• Заказные разработки на уровне ядра СУБД и ее мо-дулей расширения.

• Создание специальных модулей расширения для ре-шения прикладных и системных задач заказчика.

• Создание кастомизированных версий СУБД в интере-сах заказчика.

• Публикация изменений в основную версию кодаСУБД.

145

Организация обучения

• Обучение администраторов баз данных работе с СУБДPostgreSQL.

• Обучение разработчиков и архитекторов прикладныхсистем особенностям СУБД PostgreSQL и эффективно-му использованию ее достоинств.

• Информирование о новой функциональности и важ-ных изменениях в новых версиях.

• Проведение семинаров по разбору проектов заказ-чиков.

146