> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Движок PostgreSQL позволяет выполнять запросы `SELECT` и `INSERT` к данным, хранящимся на удалённом сервере PostgreSQL.

# Движок таблицы PostgreSQL

Движок PostgreSQL позволяет выполнять запросы `SELECT` и `INSERT` к данным, хранящимся на удалённом сервере PostgreSQL.

<Note>
  В настоящее время движок таблицы поддерживает только PostgreSQL версии 12 и выше.
</Note>

<Tip>
  Ознакомьтесь с нашим сервисом [Managed Postgres](/docs/ru/products/managed-postgres/overview). Благодаря NVMe-хранилищу, физически размещённому рядом с вычислительными ресурсами, он обеспечивает до 10 раз более высокую производительность для рабочих нагрузок, ограниченных скоростью диска, по сравнению с альтернативами, использующими сетевые хранилища, такие как EBS, а также позволяет реплицировать данные из Postgres в ClickHouse с помощью коннектора Postgres CDC в ClickPipes.
</Tip>

<div id="creating-a-table">
  ## Создание таблицы
</div>

```sql theme={null}
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 type1 [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 type2 [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = PostgreSQL({host:port, database, table, user, password[, schema, [, on_conflict]] | named_collection[, option=value [,..]]})
SETTINGS
    [ postgresql_connection_pool_size=16, ]
    [ postgresql_connection_pool_wait_timeout=5000, ]
    [ postgresql_connection_pool_retries=2, ]
    [ postgresql_connection_pool_auto_close_connection=false, ]
    [ postgresql_connection_attempt_timeout=2 ]
;
```

См. подробное описание запроса [CREATE TABLE](/docs/ru/reference/statements/create/table).

Структура таблицы может отличаться от структуры исходной таблицы PostgreSQL:

* Имена столбцов должны совпадать с именами в исходной таблице PostgreSQL, но можно использовать только часть этих столбцов и в любом порядке.
* Типы столбцов могут отличаться от типов в исходной таблице PostgreSQL. ClickHouse пытается [преобразовывать](/docs/ru/reference/engines/database-engines/postgresql#data_types-support) значения в типы данных ClickHouse.
* Настройка [external\_table\_functions\_use\_nulls](/docs/ru/reference/settings/session-settings#external_table_functions_use_nulls) определяет, как обрабатывать столбцы с типом Nullable. Значение по умолчанию: 1. Если указано 0, табличная функция не создаёт столбцы с типом Nullable и вставляет значения по умолчанию вместо null. Это также применимо к значениям NULL внутри массивов.

**Параметры движка**

* `host:port` — адрес сервера PostgreSQL.
* `database` — имя удалённой базы данных.
* `table` — имя удалённой таблицы или запрос, передаваемый в PostgreSQL как есть (см. [Передача запроса вместо имени таблицы](#passing-a-query)).
* `user` — пользователь PostgreSQL.
* `password` — пароль пользователя.
* `schema` — схема таблицы, отличная от используемой по умолчанию. Необязательно.
* `on_conflict` — стратегия разрешения конфликтов. Пример: `ON CONFLICT DO NOTHING`. Необязательно. Примечание: добавление этой опции сделает вставку менее эффективной.

[Именованные коллекции](/docs/ru/concepts/features/configuration/server-config/named-collections) (доступны начиная с версии 21.11) рекомендуются для продакшн-окружения. Вот пример:

```xml theme={null}
<named_collections>
    <postgres_creds>
        <host>localhost</host>
        <port>5432</port>
        <user>postgres</user>
        <password>****</password>
        <schema>schema1</schema>
    </postgres_creds>
</named_collections>
```

Некоторые параметры можно переопределить аргументами key-value:

```sql theme={null}
SELECT * FROM postgresql(postgres_creds, table='table1');
```

<div id="settings">
  ## Настройки
</div>

Пул соединений, используемый движком таблицы `PostgreSQL` (и [табличной функцией `postgresql`](/docs/ru/reference/functions/table-functions/postgresql)), можно настроить для каждой таблицы с помощью предложения `SETTINGS`. Если настройка не указана, по умолчанию используется значение соответствующей настройки `postgresql_*` на уровне запроса.

<div id="postgresql-connection-pool-size">
  ### `postgresql_connection_pool_size`
</div>

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

Значение по умолчанию: `16`.

<div id="postgresql-connection-pool-wait-timeout">
  ### `postgresql_connection_pool_wait_timeout`
</div>

Тайм-аут операций push/pop в пуле соединений в миллисекундах, если пул пуст. `0` означает блокировку при пустом пуле.

Значение по умолчанию: `5000`.

<div id="postgresql-connection-pool-retries">
  ### `postgresql_connection_pool_retries`
</div>

Число повторных попыток операций push/pop в пуле соединений.

Значение по умолчанию: `2`.

<div id="postgresql-connection-pool-auto-close-connection">
  ### `postgresql_connection_pool_auto_close_connection`
</div>

Закрывать соединение перед возвратом в пул.

Значение по умолчанию: `false`.

<div id="postgresql-connection-attempt-timeout">
  ### `postgresql_connection_attempt_timeout`
</div>

Тайм-аут подключения в секундах для одной попытки соединения с конечной точкой PostgreSQL. Значение передаётся как параметр `connect_timeout` в URL подключения.

Значение по умолчанию: `2`.

Пример:

```sql theme={null}
CREATE TABLE pg_table
(
    `float_nullable` Nullable(Float32),
    `str` String,
    `int_id` Int32
)
ENGINE = PostgreSQL('localhost:5432', 'public', 'test', 'postgres_user', 'postgres_password')
SETTINGS postgresql_connection_pool_size = 32, postgresql_connection_pool_auto_close_connection = 1;
```

<div id="implementation-details">
  ## Подробности реализации
</div>

`SELECT`-запросы на стороне PostgreSQL выполняются как `COPY (SELECT ...) TO STDOUT` внутри PostgreSQL-транзакции в режиме только для чтения, с коммитом после каждого `SELECT`-запроса.

Простые предложения `WHERE`, такие как `=`, `!=`, `>`, `>=`, `<`, `<=` и `IN`, выполняются на сервере PostgreSQL.

Все JOIN, агрегации, сортировка, условия `IN [ array ]` и ограничение сэмплирования `LIMIT` выполняются в ClickHouse только после завершения запроса к PostgreSQL.

<div id="passing-a-query">
  ## Передача запроса вместо имени таблицы
</div>

Вместо имени таблицы аргумент `table` может содержать `SELECT`-запрос, который передаётся в PostgreSQL как есть. Структура таблицы определяется по результату запроса. Запрос можно записать либо как подзапрос, либо обернуть в функцию `query`:

```sql theme={null}
CREATE TABLE pg_table ENGINE = PostgreSQL('localhost:5432', 'test', (SELECT a, b FROM t1 JOIN t2 USING (id) WHERE a > 0), 'user', 'password');
CREATE TABLE pg_table ENGINE = PostgreSQL('localhost:5432', 'test', query('SELECT a, b FROM t1 JOIN t2 USING (id) WHERE a > 0'), 'user', 'password');
```

Это полезно, чтобы проталкивать JOIN, агрегации или любую другую обработку в PostgreSQL. Такая таблица доступна только для чтения: `INSERT` в неё не допускается. Тот же синтаксис поддерживается табличной функцией [`postgresql`](/docs/ru/reference/functions/table-functions/postgresql).

<Note>
  Форма подзапроса `(SELECT ...)` разбирается ClickHouse и повторно сериализуется в диалекте PostgreSQL (экранирование идентификаторов PostgreSQL и строковых литералов) перед отправкой на сервер. Поэтому она должна быть корректным ClickHouse SQL. Чтобы передать синтаксис, специфичный для PostgreSQL, который ClickHouse не разбирает, используйте форму `query('...')`, текст которой отправляется в PostgreSQL дословно.

  Любой внешний `WHERE`, `LIMIT`, агрегация и т. д. из окружающего запроса ClickHouse **не** проталкиваются в переданный запрос — они применяются в ClickHouse после получения полного результата запроса. Чтобы ограничить данные, читаемые из PostgreSQL, поместите фильтр внутрь переданного запроса. При [`external_table_strict_query = 1`](/docs/ru/reference/settings/session-settings#external_table_strict_query) внешний фильтр, который нельзя протолкнуть, отклоняется с исключением вместо локального применения.
</Note>

`INSERT`-запросы на стороне PostgreSQL выполняются как `COPY "table_name" (field1, field2, ... fieldN) FROM STDIN` внутри PostgreSQL-транзакции с автокоммитом после каждого оператора `INSERT`.

Типы `Array` в PostgreSQL преобразуются в массивы ClickHouse.

<Note>
  Будьте внимательны: в PostgreSQL массив, созданный как `type_name[]`, может содержать многомерные массивы с разным количеством измерений в разных строках одного и того же столбца таблицы. В ClickHouse же допускаются только многомерные массивы с одинаковым количеством измерений во всех строках одного и того же столбца.
</Note>

Поддерживается несколько реплик, которые должны быть перечислены через `|`. Например:

```sql theme={null}
CREATE TABLE test_replicas (id UInt32, name String) ENGINE = PostgreSQL(`postgres{2|3|4}:5432`, 'clickhouse', 'test_replicas', 'postgres', 'mysecretpassword');
```

Поддерживается задание приоритета реплик для источника словаря PostgreSQL. Чем больше число в map, тем ниже приоритет. Наивысший приоритет — `0`.

В примере ниже у реплики `example01-1` наивысший приоритет:

```xml theme={null}
<postgresql>
    <port>5432</port>
    <user>clickhouse</user>
    <password>qwerty</password>
    <replica>
        <host>example01-1</host>
        <priority>1</priority>
    </replica>
    <replica>
        <host>example01-2</host>
        <priority>2</priority>
    </replica>
    <db>db_name</db>
    <table>table_name</table>
    <where>id=10</where>
    <invalidate_query>SQL_QUERY</invalidate_query>
</postgresql>
</source>
```

<div id="usage-example">
  ## Пример использования
</div>

<div id="table-in-postgresql">
  ### Таблица в PostgreSQL
</div>

```text theme={null}
postgres=# CREATE TABLE "public"."test" (
"int_id" SERIAL,
"int_nullable" INT NULL DEFAULT NULL,
"float" FLOAT NOT NULL,
"str" VARCHAR(100) NOT NULL DEFAULT '',
"float_nullable" FLOAT NULL DEFAULT NULL,
PRIMARY KEY (int_id));

CREATE TABLE

postgres=# INSERT INTO test (int_id, str, "float") VALUES (1,'test',2);
INSERT 0 1

postgresql> SELECT * FROM test;
int_id | int_nullable | float | str  | float_nullable
--------+--------------+-------+------+----------------
       1 |              |     2 | test |
(1 row)
```

<div id="creating-table-in-clickhouse-and-connecting-to--postgresql-table-created-above">
  ### Создание таблицы в ClickHouse и подключение к таблице PostgreSQL, созданной выше
</div>

В этом примере используется [движок таблицы PostgreSQL](/docs/ru/reference/engines/table-engines/integrations/postgresql), чтобы подключить таблицу ClickHouse к таблице PostgreSQL и выполнять в базе данных PostgreSQL операторы SELECT и INSERT:

```sql theme={null}
CREATE TABLE default.postgresql_table
(
    `float_nullable` Nullable(Float32),
    `str` String,
    `int_id` Int32
)
ENGINE = PostgreSQL('localhost:5432', 'public', 'test', 'postgres_user', 'postgres_password');
```

<div id="inserting-initial-data-from-postgresql-table-into-clickhouse-table-using-a-select-query">
  ### Вставка исходных данных из таблицы PostgreSQL в таблицу ClickHouse с помощью запроса SELECT
</div>

[Табличная функция postgresql](/docs/ru/reference/functions/table-functions/postgresql) копирует данные из PostgreSQL в ClickHouse. Это часто используют, чтобы повысить производительность запросов, выполняя запросы и аналитику в ClickHouse, а не в PostgreSQL; также её можно использовать для миграции данных из PostgreSQL в ClickHouse. Поскольку мы будем копировать данные из PostgreSQL в ClickHouse, используем в ClickHouse движок таблицы MergeTree и назовём её postgresql\_copy:

```sql theme={null}
CREATE TABLE default.postgresql_copy
(
    `float_nullable` Nullable(Float32),
    `str` String,
    `int_id` Int32
)
ENGINE = MergeTree
ORDER BY (int_id);
```

```sql theme={null}
INSERT INTO default.postgresql_copy
SELECT * FROM postgresql('localhost:5432', 'public', 'test', 'postgres_user', 'postgres_password');
```

<div id="inserting-incremental-data-from-postgresql-table-into-clickhouse-table">
  ### Вставка инкрементальных данных из таблицы PostgreSQL в таблицу ClickHouse
</div>

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

Для этого потребуется отслеживать максимальный ID или временную метку, добавленные ранее, например:

```sql theme={null}
SELECT max(`int_id`) AS maxIntID FROM default.postgresql_copy;
```

Затем вставка значений из таблицы PostgreSQL, превышающих максимальное значение

```sql theme={null}
INSERT INTO default.postgresql_copy
SELECT * FROM postgresql('localhost:5432', 'public', 'test', 'postgres_user', 'postgres_password')
WHERE int_id > (SELECT max(int_id) FROM default.postgresql_copy);
```

<div id="selecting-data-from-the-resulting-clickhouse-table">
  ### Выборка данных из итоговой таблицы ClickHouse
</div>

```sql theme={null}
SELECT * FROM postgresql_copy WHERE str IN ('test');
```

```text theme={null}
┌─float_nullable─┬─str──┬─int_id─┐
│           ᴺᵁᴸᴸ │ test │      1 │
└────────────────┴──────┴────────┘
```

<div id="using-non-default-schema">
  ### Использование нестандартной схемы
</div>

```text theme={null}
postgres=# CREATE SCHEMA "nice.schema";

postgres=# CREATE TABLE "nice.schema"."nice.table" (a integer);

postgres=# INSERT INTO "nice.schema"."nice.table" SELECT i FROM generate_series(0, 99) as t(i)
```

```sql theme={null}
CREATE TABLE pg_table_schema_with_dots (a UInt32)
        ENGINE PostgreSQL('localhost:5432', 'clickhouse', 'nice.table', 'postgrsql_user', 'password', 'nice.schema');
```

**См. также**

* [Табличная функция `postgresql`](/docs/ru/reference/functions/table-functions/postgresql)
* [Использование PostgreSQL в качестве источника для словаря](/docs/ru/reference/statements/create/dictionary/sources/postgresql)

<div id="related-content">
  ## Связанные материалы
</div>

* Блог: [ClickHouse и PostgreSQL — идеальная пара для работы с данными — часть 1](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres)
* Блог: [ClickHouse и PostgreSQL — идеальная пара для работы с данными — часть 2](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres-part-2)
