Database reports often need a CSV file for a spreadsheet or another application. SELECT … INTO OUTFILE exports query results from MySQL or MariaDB to a CSV file on the database server.

The statement can define the field delimiter, quoting, escape behavior, line ending, and character set so the exported file matches the system that will consume it. The export shown here writes comma-separated rows with quoted fields and doubled embedded quote characters, which is the layout many spreadsheet and import tools expect.

The file is created by the database server process, not by the mysql or mariadb command-line client. Exporting therefore requires SELECT access to the data, the global FILE privilege, a destination the server can write to, and a new filename. Managed database services may withhold FILE or server filesystem access; an ordinary mysqldump SQL backup is not a CSV file.

Steps to export MySQL or MariaDB data to CSV:

  1. Log in with a database account authorized to export the selected data.
    $ mysql -u export_user -p
    Enter password:
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    ##### snipped #####
    mysql>

    For MariaDB, the client command is mariadb -u export_user -p. The remaining SQL works in either client. Reading the finished file requires a shell on the database host; the example uses sudo. A remote SQL client still writes the file on the server.

  2. Select the database that contains the rows to export.
    mysql> USE appdb;
    ##### snipped #####
    Database changed

    The active database determines which table the export reads.

  3. Review the table columns before building the export query.
    mysql> DESCRIBE orders;
    +---------------+---------------+------+-----+---------+----------------+
    | Field         | Type          | Null | Key | Default | Extra          |
    +---------------+---------------+------+-----+---------+----------------+
    | id            | int           | NO   | PRI | NULL    | auto_increment |
    | customer_name | varchar(100)  | NO   |     | NULL    |                |
    | total         | decimal(10,2) | NO   |     | NULL    |                |
    | created_at    | datetime      | NO   |     | NULL    |                |
    +---------------+---------------+------+-----+---------+----------------+
    4 rows in set (0.00 sec)

    An explicit column list keeps the export stable when the table schema changes later.

  4. Preview the exact rows that belong in the export file.
    mysql> SELECT id, customer_name, total, created_at
        ->   FROM orders
        ->   ORDER BY id
        ->   LIMIT 5;
    +----+---------------+--------+---------------------+
    | id | customer_name | total  | created_at          |
    +----+---------------+--------+---------------------+
    |  1 | Alice         | 249.99 | 2026-04-09 10:00:00 |
    |  2 | Bob, Inc.     |  89.50 | 2026-04-09 10:05:00 |
    |  3 | Carol "Prime" | 149.00 | 2026-04-09 10:10:00 |
    +----+---------------+--------+---------------------+
    3 rows in set (0.00 sec)

    The table and rows are examples. A WHERE or LIMIT clause added here belongs in the export query as well.

  5. Check the active secure_file_priv setting before choosing the output path.
    mysql> SHOW VARIABLES LIKE 'secure_file_priv';
    +------------------+-----------------------+
    | Variable_name    | Value                 |
    +------------------+-----------------------+
    | secure_file_priv | /var/lib/mysql-files/ |
    +------------------+-----------------------+
    1 row in set (0.00 sec)

    A directory value restricts exports to that location, while a blank value leaves paths unrestricted by secure_file_priv but still subject to server filesystem permissions. MySQL also supports NULL to disable server-side import and export. If the value differs, the paths in the SQL and shell commands must match the allowed location.
    Related: How to fix secure-file-priv errors in MySQL or MariaDB

  6. Export the result set to a new CSV file.

    The file contains the selected database values, so its storage and access permissions must suit that data.

    mysql> SELECT id, customer_name, total, created_at
        ->   FROM orders
        ->   ORDER BY id
        ->   INTO OUTFILE '/var/lib/mysql-files/orders-export.csv'
        ->   CHARACTER SET utf8mb4
        ->   FIELDS TERMINATED BY ','
        ->   ENCLOSED BY '"'
        ->   ESCAPED BY '"'
        ->   LINES TERMINATED BY '\n';
    Query OK, 3 rows affected (0.00 sec)

    ESCAPED BY '"' doubles any quote inside a field, the way CSV expects. It also writes a NULL as "N and leaves a stray quote at a line break inside a field, so wrap a column that can hold either, here one named notes: COALESCE(notes, '') writes NULL as an empty field and REPLACE(notes, '\n', ' ') keeps the value on one line.

    INTO OUTFILE writes data rows only. To add a header row, join a SELECT of the column names to the export query with UNION ALL, and leave out ORDER BY because it would sort the header in with the data.

    mysql> SELECT 'id', 'customer_name', 'total', 'created_at'
        ->   UNION ALL
        -> SELECT id, customer_name, total, created_at
        ->   FROM orders
        ->   INTO OUTFILE '/var/lib/mysql-files/orders-with-header.csv'
        ->   CHARACTER SET utf8mb4
        ->   FIELDS TERMINATED BY ','
        ->   ENCLOSED BY '"'
        ->   ESCAPED BY '"'
        ->   LINES TERMINATED BY '\n';
    Query OK, 4 rows affected (0.00 sec)

    An existing filename fails with ERROR 1086, a path outside secure_file_priv fails with ERROR 1290, and an account without the global FILE privilege fails with ERROR 1227. Use a new filename or an allowed path instead of deleting an earlier export or loosening the restriction. FILE permits server-side file access beyond one table, so an administrator must approve it.

  7. Print the exported file on the database host.
    $ sudo cat /var/lib/mysql-files/orders-export.csv
    "1","Alice","249.99","2026-04-09 10:00:00"
    "2","Bob, Inc.","89.50","2026-04-09 10:05:00"
    "3","Carol ""Prime""","149.00","2026-04-09 10:10:00"
Share this guide