Установите postgresql на любую виртуальную машину. Перейдите в пользователя `postgres`.
```
$ sudo su - postgres
@ -8,17 +8,17 @@ $ sudo su - postgres
Вы суперпользователь базы данных.
## 2. Основы
В`psql` выполните инициализацю таблиц базы данных
## 2. Знакомство с командной строкой psql
В`psql` выполните инициализацию таблиц базы данных
```
\i scripts/weather.sql
```
Первая команда, которую нужно знать, это как получить помощь по командам:
Первая команда, которую нужно знать, это команда помощи:
```
\?
```
Вы увидите страницу общей помощи по командам терминала. Результаты будут на разбиты на страницы, которые можно пролистать клавишей пробел.
Вы увидите страницу общей помощи по командам терминала. Результаты будут разбиты на страницы, которые можно пролистать клавишей пробел.
Чтобы вывести список возможных SQL команд, наберите
```
@ -35,7 +35,7 @@ $ sudo su - postgres
\l
```
Из командой строки `psql`даже можно выполнить команду linux. Выведите список файлов корневого каталога операционной системы
Из командой строки `psql`также можно выполнить команду linux. Например список файлов корневого каталога операционной системы
```
\! ls -la
```
@ -55,14 +55,14 @@ $ sudo su - postgres
- `\df` - вывод всех функций,
- `\dp` - вывод привилегий доступа для таблиц и представлений.
## 2.2 Пейджер
### 2.2 Пейджер
По умолчанию пейджер `psql`, который разбивает вывод на страницы, включён. Вы можете отключить его установить значение параметра `pager` равным 0.
По умолчанию пейджер `psql`, который разбивает вывод на страницы, включён. Вы можете отключить его установив значение параметра `pager` равным 0.
```
\pset pager 1
\pset pager 0
```
## 2.3 SQL запросы
## 3. Язык SQL
Попробуйте дать базе данных простой запрос
```sql
@ -70,8 +70,6 @@ SELECT event_type FROM weather LIMIT 20;
```
Это запрос на получение данных колонки `event_type` из таблицы `weather`. Количество результатов будет ограничено 20 записями.
### 2.3.1 Буфер многострочного запроса
В терминале вы также можете написать многострочный запрос. Вводите запрос как обычно, используя Enter для переноса строки. `psql` создаст так называемый буфер запроса. Запрос не будет интерпретирован, пока вы не введёте символ `;`. Чтобы сбросить буфер при ошибке ввода, используйте `\r`.
```sql
\r
@ -80,7 +78,7 @@ FROM weather
WHERE state = 'HAWAII';
```
### 2.3.2 Замер времени выполнения запроса
### 3.1 Замер времени выполнения запроса
Вы можете замерить время выполнения запроса, включив его командой `\timing`
```sql
@ -88,7 +86,7 @@ WHERE state = 'HAWAII';
SELECT DISTINCT(event_type) FROM weather LIMIT 40;
```
### 2.4 DDL, DML запросы
### 3.2 Data Definition Language (DDL), Data Modification Language (DML) запросы
SQL запросы, связанные с модификацией данных таблиц называют DML (Data Modification Language) запросами. SQL запросы, связанные с модификацией базы данных называют DDL (Data Definition Language) запросами.
@ -101,43 +99,47 @@ WHERE episode_id = 57676;
Чтобы выйти из консоли введите `\q`.
## 3. Соединения таблиц
## 4. Соединения таблиц
В реляционных базах данные разбиваются на таблицы так, чтобы избыточность хранимой информации была минимальной, запросы и долгосрочная поддержка были эффективными. Это приводит к необходимости объединять информацию из разных таблиц с помощью механизма `JOIN`.
Вследующих примерах мы рассмотрим гипотетическую базу данных для управления человеческими ресурсами. Перед выполнением выполните инициализацию базы данных
Вэтом разделе мы рассмотрим выдуманную базу данных, созданную для HR отдела для управления человеческими ресурсами. Перед выполнением выполните инициализацию базы данных
Каждый сотрудник имеет свой идентификационный номер `employee_id`. В таблице хранится также важная информация как имя, фамилия, должность и зарплата. Принадлежность к отделу выражается в наличии связи с таблицей `departments` через поле `department_id`. Поле `manager_id` ведёт к менеджеру сотрудника.
### 3.1 Внутренние соединения (inner join)
### 4.1 Внутренние соединения (inner join)
Внутреннее соединение является наиболее распространенным и соединяет столбец в одной таблице и со столбцом в другой. В нашей базе данных HR мы хотели бы найти сотрудников и их отделы. Итак, допустим, мы хотим вывести сотрудников и отделы, в которых они числятся:
@ -149,22 +151,24 @@ INNER JOIN departments d ON (e.department_id = d.department_id);
Мы обратились к СУБД на специальном языке и СУБД взяла на себя самую сложную часть, избавив нас от необходимости писать специализированную программу для поиска и выгрузки информации.
### 3.2 Левые внешние соединения (left outer join)
### 4.2 Левые внешние соединения (left outer join)
При использовании механизма `LEFT OUTER JOIN` из левой таблицы вернуться все строки, и если справа не найдётся соответсвия, то в столбцах правой таблицы вместо значений будет присутствовать `NULL`.
В базе данных, с которой мы работаем младшие сотрудники зависят от старших. Ниже приведена схема третьей таблицы `dependents`, которая фиксирует эти отношения.
Допустим, что кроме отдела сотрудника нам также нужно вывести его подчинённых:
@ -179,16 +183,18 @@ FROM employees e
Если бы мы использовали `INNER JOIN`, то исключили бы обычных сотрудников из результатов запроса.
### 3.3 Правые внешние соединения (right outer join)
### 4.3 Правые внешние соединения (right outer join)
В ситуации с`RIGHT OUTER JOIN` всё происходит наоборот. При отсутствии соответствующей записи слева для записи таблицы указанной справа значениями `NULL` заполняются столбцы левой таблицы.
Ранее в схеме departments мы видели, что каждый отдел связан с географическим положением через поле `location_id` таблицы Местоположения (locations).
Схема таблицы Местоположения (locations)
```
\d locations
```
Схема таблицы Местоположения (locations)
| Колонка |
|----------------|
| location_id |
@ -206,11 +212,27 @@ FROM departments d
RIGHT OUTER JOIN locations l ON (d.location_id = l.location_id);
```
Результат говорят нам сообщают, что в городе Effingham штата Индиана США нет отдела. Значит офис был закрыт.
Результат нам сообщают, что в городе Effingham штата Индиана США нет отдела. Значит офис был закрыт.
### 4.4 Cоединения на себя (self join)
Соединение на себя - вид соединения, хотя и не имеет специального синтаксиса, стоит упомянуть отдельно. Такой способ соединения полезен, когда таблица содержит иерархические данные. В нашем примере это таблица `employees`.
employee_id | integer | | not null | generated by default as identity
first_name | character varying(20) | | |
last_name | character varying(25) | | not null |
start_date | date | | not null | CURRENT_DATE
job_title | character varying(100) | | not null |
salary | numeric(9,2) | | not null |
manager_id | integer | | |
department_id | integer | | |
```
Соединение на себя - это не технический, а концептуальный термин, который стоит упомянуть отдельно. Такой способ соединения полезен, когда таблица содержит иерархические данные. В нашем примере это таблица `employees`. Обратите внимание, что каждому сотруднику назначен менеджер `manager_id`, который в свою очередь сам является сотрудником, а значит иметь своего менеджера уровнем выше.
Обратите внимание, что каждому сотруднику назначен менеджер `manager_id`, который в свою очередь сам является сотрудником, а значит имеет вышестоящего менеджера, если только он не основатель компании.
Используя соединение на себя мы можем запросить у базы данных список сотрудников и их менеджеров
@ -222,7 +244,7 @@ FROM employees e LEFT OUTER JOIN employees m ON (e.manager_id = m.employee_id);
Поздравляем! Вы познакомились с наиболее типичными видами соединений таблиц.
## 4. Транзакции
## 5. Транзакции
Транзакия - это набор действий, которые выполняются атомарно для внешнего наблюдателя. Если часть действий выполнится не может, откатываются все изменения транзакции.
@ -241,7 +263,7 @@ FROM employees e LEFT OUTER JOIN employees m ON (e.manager_id = m.employee_id);
Транзакции позволяют не писать логику возврата в предыдущее состояние, до начала транзакции.
### 4.1 Простейшие транзакции
### 5.1 Простейшие транзакции
Самая простая транзакия состоит из одного запроса на модификацию данны, заключенного между `BEGIN` и `COMMIT`.
```sql
@ -288,7 +310,7 @@ BEGIN;
COMMIT;
```
### 4.2 Комплексные транзакции
### 5.2 Комплексные транзакции
В реальной жизни транзакции состоят из множества действий.
```sql
@ -318,7 +340,7 @@ COMMIT;
Проверьте, что Bob Young не добавился.
### 4.3 Область видимости изменений транзакции
### 5.3 Область видимости изменений транзакции
Что произойдёт, если вы запустили транзакцию, а в этом время к данным, которые вы модифицируете в новой транзакции обратился другой пользователь? Изменения в транзакциях невидимы другим транзакциям, пока транзакция производящие измнения не завершились.
@ -345,7 +367,7 @@ COMMIT;
Такой вид транзакций ещё называют транзакционным язык описания данных (Transactional DDL). Они жизненно важны для миграций баз данных.
### 4.4 Продвинутые транзакции с вложенными точками отката
### 5.4 Продвинутые транзакции с вложенными точками отката
Для некоторый специфичный случаев могут потребоваться вложенные транзакции. Postgres позволяет сделать даже больше - делать точки сохранения внутри транзакции. Точка сохранения (SAVEPOINT) предоставляет указатель на который может ссылаться комадна `ROLLBACK`.
```sql
@ -364,15 +386,213 @@ COMMIT;
Теперь, если вы попробуете найти Bob Young - вы найдёте его, но не найдёте его подчинённых. Что случилось? Эту транзакцию стало возможным выполнить частично из-за использования команды `SAVEPOINT`.
### 4.5 Я в транзакции?
### 5.5 Я в транзакции?
Чтобы узнать идентификатор транзакции, в которой вы находитесь, вызовите команду
```
```sql
SELECT txid_current();
```
Вы можете удивиться, ведь вы не запускали транзакцию. На самом деле Postgres выполняет все команды, даже самые простые в контексте транзакций.
## 6. Индексы
### 6.1 B-Tree
Познакомимся с наиболее распространённым индексом - B-Tree индексом - на примере тестовых данных `weather.sql`.
Попробуем найти всё про снежные бури.
```sql
SELECT *
FROM weather
WHERE event_type = 'Winter Storm';
```
Чтобы проанализировать запрос используйте `EXPLAIN ANALYZE`
```sql
EXPLAIN ANALYZE
SELECT *
FROM weather
WHERE event_type = 'Winter Storm' limit 2;
```
Анализ покажет вам время планирования выполнения запроса `Planning Time` и время выполнения `Execution Time`.
-> Bitmap Index Scan on idx_weather_event_type (cost=0.00..4.36 rows=11 width=0) (actual time=0.025..0.025 rows=11 loops=1)
Index Cond: ((event_type)::text = 'Winter Storm'::text)
Planning Time: 0.234 ms
Execution Time: 0.057 ms
(7 rows)
```
### 6.2 Многоколоночный индекс
Postgres позволяет создавать сложные индексы для нескольких колонок. Как и в случае с одной колонкой, если вы выполняете запрос по нескольких колонкам часто, такой индекс позволит выполнять его эффективнее.
Удалите старый индекс командой `DROP INDEX idx_weather_event_type;`. Выполним другой запрос - найдём снежные бури, которые привели к потере урожая
Идеальном случае мы хотим видеть `99%` для таблиц больших чем 10000 записей. Если вы нашли такую таблицу, где это не так - она хороший кандидат для создания новых индексов.
Также полезной может быть информация о том, сколько блоков индексированных данных читается из кэша Postgres, а сколько с диска.
```sql
SELECT
sum(idx_blks_read) as idx_read,
sum(idx_blks_hit) as idx_hit,
(sum(idx_blks_hit) - sum(idx_blks_read)) / sum(idx_blks_hit) as ratio
FROM pg_statio_user_indexes;
```
Упомянем ещё пару хитростей.
Использование команды `CREATE INDEX CONCURRENTLY` позволит избежать блокировки таблицы.
Параметр `maintenance_work_mem` определяет объём памяти задействованный под процесс создания индексов и другие фоновые задачи, например `vacuum`. По умолчанию это значение равно 64 МБ (вы можете найти его из psql в списке `select name, setting from pg_settings;`). Подумайте о том, чтобы увеличить это значение перед созданием больших индексов.
## 7. Проблема разбухания БД Postgres
У Postgres есть особый способ обработки обновлений или удалений. При удалении данных место на диске не освобождается до тех пор, пока база данных не будет запущена специальный процесс очистки `vacuum`. Если база данных загружена большим количеством DML запросов на удаление строк, через какое-то время в ней появится много неиспользуемого пространства, что негативно повлияет на производительность, если ничего не сделать.
Попробуйте следующий запрос https://wiki.postgresql.org/wiki/Show_database_bloat, чтобы увидеть находится ли база данных в оптимальном или раздутом (bloat) состоянии.