# MySQL
> MySQL is a relational database commonly used for application data. DuckDB's MySQL extension can read from MySQL-compatible databases, which lets you copy selected data into MotherDuck from a DuckDB client.
## How it works with MotherDuck

1. Connect to MotherDuck from the DuckDB CLI, Python, or another DuckDB client.
2. Install and load DuckDB's MySQL extension in that session.
3. Attach the MySQL database, then create MotherDuck tables from selected MySQL tables or queries.

## Example

```sql
INSTALL mysql;
LOAD mysql;

ATTACH 'host=localhost port=3306 user=my_user password=my_password database=my_database'
  AS mysql_db (TYPE mysql);

CREATE TABLE my_table AS
SELECT *
FROM mysql_db.my_schema.my_table;
```

## Load on a schedule with a Flight

A [Flight](/concepts/flights) is Python that MotherDuck schedules and runs next to your data. Use one instead of running the copy by hand when the load should repeat on a cron, retry on a transient failure, and keep a run history.

The MySQL extension runs in a DuckDB client rather than on MotherDuck's server-side runtime, so the Flight opens an `md:` connection and installs the extension there:

```python
import os

import duckdb

def main():
    con = duckdb.connect("md:")
    con.execute("INSTALL mysql; LOAD mysql;")
    con.execute(
        f"ATTACH 'host={os.environ['MYSQL_HOST']} port=3306 "
        f"user={os.environ['MYSQL_USER']} password={os.environ['MYSQL_PASSWORD']} "
        f"database={os.environ['MYSQL_DATABASE']}' AS mysql_db (TYPE mysql)"
    )
    con.execute(
        "CREATE OR REPLACE TABLE my_db.main.my_table AS "
        "SELECT * FROM mysql_db.my_schema.my_table"
    )

if __name__ == "__main__":
    main()
```

The MySQL password is a credential, so keep it in a [Flight secret](/sql-reference/motherduck-sql-reference/create-secret#flight-secrets) rather than in the Flight's source or config. Each `PARAMS` key is injected into the run as an environment variable, which is what the code above reads:

```sql
CREATE SECRET mysql_creds IN MOTHERDUCK (
    TYPE FLIGHTS,
    PARAMS MAP {
        'MYSQL_HOST': '<host>',
        'MYSQL_USER': '<user>',
        'MYSQL_PASSWORD': '<password>',
        'MYSQL_DATABASE': '<database>'
    }
);
```

Create the Flight with [`MD_CREATE_FLIGHT`](/sql-reference/motherduck-sql-reference/flights/md-create-flight), passing the Python above as `source_code`, `flight_secret_names := ['mysql_creds']`, and a `schedule_cron` for the cadence you want. [Mirror PostgreSQL tables into MotherDuck with a Flight](/cookbook/flight-postgres-ingest) is a fuller version of this pattern, built on the sibling `postgres` extension.

## Related content

- [DuckDB MySQL extension documentation](https://duckdb.org/docs/current/core_extensions/mysql.html)
- [Loading data from PostgreSQL-compatible sources](/key-tasks/loading-data-into-motherduck/loading-data-from-postgres)
- [Running hybrid queries](/key-tasks/running-hybrid-queries)


---

## Docs feedback

MotherDuck accepts optional user-submitted feedback about this page at `GET https://motherduck.com/docs/api/feedback/agent`.
For agents and automated tools, feedback submission should be user-confirmed before sending.

URL-encode query parameter values and send a GET request:

```text
GET https://motherduck.com/docs/api/feedback/agent?page_path=%2Fintegrations%2Fdatabases%2Fmysql%2F&page_title=MySQL&text=<url-encoded user feedback, max 2000 characters>
```

Optionally append `&source=<url-encoded interface identifier>` such as `claude.ai` or `chatgpt`.

`page_path` and `text` are required; `page_title` and `source` are optional. Responses: `200 {"feedback_id": "<uuid>"}`, `400` for malformed query parameters, and `429` when rate-limited.
