> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-revert-104359-revert-104251-parquet-single.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Allows `SELECT` and `INSERT` queries to be performed on data that are stored on a remote MySQL server.

# mysql

Allows `SELECT` and `INSERT` queries to be performed on data that are stored on a remote MySQL server.

<h2 id="syntax">
  Syntax
</h2>

```sql theme={null}
mysql({host:port, database, table, user, password[, replace_query, on_duplicate_clause] | named_collection[, option=value [,..]]})
```

<h2 id="arguments">
  Arguments
</h2>

| Argument              | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `host:port`           | MySQL server address.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `database`            | Remote database name.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `table`               | Remote table name, or a query passed to MySQL as is (see [Passing a query instead of a table name](#passing-a-query)).                                                                                                                                                                                                                                                                                                                                                                                                               |
| `user`                | MySQL user.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `password`            | User password.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `replace_query`       | Flag that converts `INSERT INTO` queries to `REPLACE INTO`. Possible values:<br />    - `0` - The query is executed as `INSERT INTO`.<br />    - `1` - The query is executed as `REPLACE INTO`.                                                                                                                                                                                                                                                                                                                                      |
| `on_duplicate_clause` | The `ON DUPLICATE KEY on_duplicate_clause` expression that is added to the `INSERT` query. Can be specified only with `replace_query = 0` (if you simultaneously pass `replace_query = 1` and `on_duplicate_clause`, ClickHouse generates an exception).<br />    Example: `INSERT INTO t (c1,c2) VALUES ('a', 2) ON DUPLICATE KEY UPDATE c2 = c2 + 1;`<br />    `on_duplicate_clause` here is `UPDATE c2 = c2 + 1`. See the MySQL documentation to find which `on_duplicate_clause` you can use with the `ON DUPLICATE KEY` clause. |

Arguments also can be passed using [named collections](/concepts/features/configuration/server-config/named-collections). In this case `host` and `port` should be specified separately. This approach is recommended for production environment.

Simple `WHERE` clauses such as `=, !=, >, >=, <, <=` are currently executed on the MySQL server.

The rest of the conditions and the `LIMIT` sampling constraint are executed in ClickHouse only after the query to MySQL finishes.

<h2 id="tls-ssl">
  TLS/SSL
</h2>

The credentials of an encrypted connection to MySQL are passed as named collection keys (or as key-value arguments):

| Parameter      | Description                                                                           |
| -------------- | ------------------------------------------------------------------------------------- |
| `ssl_ca_pem`   | Contents of the CA certificate that the MySQL server certificate is verified against. |
| `ssl_cert_pem` | Contents of the client certificate, for certificate-based authentication.             |
| `ssl_key_pem`  | Contents of the private key belonging to `ssl_cert_pem`.                              |

The values are the contents of the corresponding PEM files, which can be copied into a named collection or into a query. They are masked in logs and in `SHOW` queries, the same way passwords are.

The same credentials can also be given as paths to files on the server, in `ssl_ca`, `ssl_cert` and `ssl_key` — but **only in a named collection defined in the server configuration file**, and such a value cannot be overridden in a query. The server opens those files with its own privileges, so accepting a path from SQL would let any user who is able to define a MySQL source probe the local filesystem, and authenticate with a certificate and key they are not allowed to read themselves.

<h2 id="passing-a-query">
  Passing a query instead of a table name
</h2>

Instead of a table name, the third argument can be a `SELECT` query that is passed to MySQL as is. The structure of the resulting table is inferred from the query result. The query can be written either as a subquery, or wrapped into the `query` function:

```sql theme={null}
SELECT * FROM mysql('localhost:3306', 'test', (SELECT a, b FROM t1 JOIN t2 USING (id) WHERE a > 0), 'user', 'password');
SELECT * FROM mysql('localhost:3306', 'test', query('SELECT a, b FROM t1 JOIN t2 USING (id) WHERE a > 0'), 'user', 'password');
```

This is useful to push down joins, aggregations or any other processing to MySQL. Such a table is read-only: `INSERT` into it is not allowed. The same syntax is supported by the [`MySQL`](/reference/engines/table-engines/integrations/mysql) table engine.

<Note>
  The subquery form `(SELECT ...)` is parsed by ClickHouse and re-serialized in the MySQL dialect (backtick identifier quoting) before being sent to the server. It must therefore be valid ClickHouse SQL. To pass MySQL-specific syntax that ClickHouse does not parse, use the `query('...')` form, whose text is sent to MySQL verbatim.

  Any outer `WHERE`, `LIMIT`, aggregation, etc. of the surrounding ClickHouse query is **not** pushed down into the passed query — it is applied in ClickHouse after the full query result is fetched. To restrict the data read from MySQL, put the filter inside the passed query. With [`external_table_strict_query = 1`](/reference/settings/session-settings/external-table#external_table_strict_query) an outer filter on the columns of the table function is rejected with an exception instead of being applied locally, because it cannot be pushed into the passed query. The check covers the top-level `WHERE` predicate and each conjunct of a top-level `AND`. A `PREWHERE` on the columns of this table is not a case for this setting: this table engine do not support `PREWHERE`, and such a query is rejected with `ILLEGAL_PREWHERE` regardless of the setting. With the analyzer (the default), the check runs only where a filter could be pushed down at all: when this table is the only table of the query, on either side of an `INNER JOIN`, or on the preserving side of an outer join (the left side of a `LEFT JOIN`, the right side of a `RIGHT JOIN`). On the non-preserving side of a `LEFT`/`RIGHT JOIN` and on either side of a `FULL JOIN` nothing is pushed down and nothing is checked, so a filter on the columns of this table is applied locally after the join even in strict mode. Where the check runs, a predicate that references other tables joined in the surrounding query is not pushed down and is excluded from the check, whether it references only the joined side or mixes it with this table inside one non-`AND` expression (for example an `OR`); such a predicate keeps its usual ClickHouse evaluation point (`WHERE` after the join, `PREWHERE` before it) and is not rejected. With the old analyzer (`enable_analyzer = 0`) this scoping does not apply: the whole outer filter is checked when this table is the first table of the join tree, including a predicate on the joined side, and a joined right-hand table is not checked.
</Note>

Supports multiple replicas that must be listed by `|`. For example:

```sql theme={null}
SELECT name FROM mysql(`mysql{1|2|3}:3306`, 'mysql_database', 'mysql_table', 'user', 'password');
```

or

```sql theme={null}
SELECT name FROM mysql(`mysql1:3306|mysql2:3306|mysql3:3306`, 'mysql_database', 'mysql_table', 'user', 'password');
```

<h2 id="returned-value">
  Returned value
</h2>

A table object with the same columns as the original MySQL table.

<Note>
  Some data types of MySQL can be mapped to different ClickHouse types - this is addressed by query-level setting [mysql\_datatypes\_support\_level](/reference/settings/session-settings/mysql#mysql_datatypes_support_level)
</Note>

<Note>
  In the `INSERT` query to distinguish table function `mysql(...)` from table name with column names list, you must use keywords `FUNCTION` or `TABLE FUNCTION`. See examples below.
</Note>

<h2 id="examples">
  Examples
</h2>

Table in MySQL:

```text theme={null}
mysql> CREATE TABLE `test`.`test` (
    ->   `int_id` INT NOT NULL AUTO_INCREMENT,
    ->   `float` FLOAT NOT NULL,
    ->   PRIMARY KEY (`int_id`));

mysql> INSERT INTO test (`int_id`, `float`) VALUES (1,2);

mysql> SELECT * FROM test;
+--------+-------+
| int_id | float |
+--------+-------+
|      1 |     2 |
+--------+-------+
```

Selecting data from ClickHouse:

```sql theme={null}
SELECT * FROM mysql('localhost:3306', 'test', 'test', 'bayonet', '123');
```

Or using [named collections](/concepts/features/configuration/server-config/named-collections):

```sql theme={null}
CREATE NAMED COLLECTION creds AS
        host = 'localhost',
        port = 3306,
        database = 'test',
        user = 'bayonet',
        password = '123';
SELECT * FROM mysql(creds, table='test');
```

```text theme={null}
┌─int_id─┬─float─┐
│      1 │     2 │
└────────┴───────┘
```

<h3 id="enable-compression">
  `enable_compression`
</h3>

Enables compression for the MySQL protocol connection.

Default value: `false`.

This setting applies to:

* the `mysql` table function;
* the `MySQL` table engine;
* the `MySQL` database engine;
* named collections used by MySQL integrations.

When enabled, ClickHouse requests compression for the connection.

Example:

```sql theme={null}
SELECT *
FROM mysql(
    'mysql80:3306',
    'clickhouse',
    'test_table',
    'root',
    'password',
    SETTINGS enable_compression = 1
);
```

Replacing and inserting:

```sql theme={null}
INSERT INTO FUNCTION mysql('localhost:3306', 'test', 'test', 'bayonet', '123', 1) (int_id, float) VALUES (1, 3);
INSERT INTO TABLE FUNCTION mysql('localhost:3306', 'test', 'test', 'bayonet', '123', 0, 'UPDATE int_id = int_id + 1') (int_id, float) VALUES (1, 4);
SELECT * FROM mysql('localhost:3306', 'test', 'test', 'bayonet', '123');
```

```text theme={null}
┌─int_id─┬─float─┐
│      1 │     3 │
│      2 │     4 │
└────────┴───────┘
```

Copying data from MySQL table into ClickHouse table:

```sql theme={null}
CREATE TABLE mysql_copy
(
   `id` UInt64,
   `datetime` DateTime('UTC'),
   `description` String,
)
ENGINE = MergeTree
ORDER BY (id,datetime);

INSERT INTO mysql_copy
SELECT * FROM mysql('host:port', 'database', 'table', 'user', 'password');
```

Or if copying only an incremental batch from MySQL based on the max current id:

```sql theme={null}
INSERT INTO mysql_copy
SELECT * FROM mysql('host:port', 'database', 'table', 'user', 'password')
WHERE id > (SELECT max(id) FROM mysql_copy);
```

<h2 id="related">
  Related
</h2>

* [The 'MySQL' table engine](/reference/engines/table-engines/integrations/mysql)
* [Using MySQL as a dictionary source](/reference/statements/create/dictionary/sources/mysql)
* [mysql\_datatypes\_support\_level](/reference/settings/session-settings/mysql#mysql_datatypes_support_level)
* [mysql\_map\_fixed\_string\_to\_text\_in\_show\_columns](/reference/settings/session-settings/mysql-map#mysql_map_fixed_string_to_text_in_show_columns)
* [mysql\_map\_string\_to\_text\_in\_show\_columns](/reference/settings/session-settings/mysql-map#mysql_map_string_to_text_in_show_columns)
* [mysql\_max\_rows\_to\_insert](/reference/settings/session-settings/mysql#mysql_max_rows_to_insert)
