Skip to content

Guide

How to export SQL query results to CSV automatically

scheduled SQL exports / CSV / SQL Server / PostgreSQL / MySQL

Automate a recurring SQL to CSV export on SQL Server, PostgreSQL, MySQL, Azure SQL and Redshift: native options, where each breaks, what is simpler.

Exporting a query result to CSV once is a right click in any SQL client. Doing it every morning at seven, without anyone opening the client, is a different problem, and the forum threads asking how to do it go back fifteen years because every database answers it differently.

Every automated export has the same four parts: something that runs on a schedule, something that executes the query, something that writes a correct CSV, and something that delivers the file to a person. Databases are good at the second part and uneven at the other three.

What each database gives you

Database Scheduler inside the database Writes a file Sends it
SQL Server (Standard, Enterprise) SQL Server Agent sp_send_dbmail attachment, bcp, SSIS Database Mail
SQL Server Express none sqlcmd, bcp from outside none
Azure SQL Database Elastic Jobs none none
PostgreSQL pg_cron extension COPY on the server, \copy from psql none
MySQL Event Scheduler SELECT ... INTO OUTFILE on the server none
Amazon Redshift Query Editor v2 schedules UNLOAD to S3 none

The table explains why almost every working solution ends up outside the database: a scheduler on another machine, a client that runs the query and writes the file there, and a mail relay or chat webhook to deliver it.

The per database details

The traps are specific enough that each engine has its own guide:

  • SQL Server with sp_send_dbmail: the parameters that make the attachment readable, the missing CSV quoting and the 1 MB default attachment limit.
  • SQL Server Express: Task Scheduler, Invoke-Sqlcmd with Export-Csv, and why Send-MailMessage fails against Microsoft 365.
  • Azure SQL Database: no Agent and no Database Mail, so Logic Apps, Functions or an agent.
  • PostgreSQL: COPY versus \copy, why pg_cron alone produces nothing a person can open, and what changes on RDS and Cloud SQL.
  • MySQL: the Event Scheduler, secure_file_priv, the missing header row in INTO OUTFILE, and the client-side alternative.

The four things that break in every homemade setup

Quoting. A CSV is only correct if values with commas, quotes and line breaks are quoted. COPY ... WITH (FORMAT csv), PowerShell Export-Csv and MySQL Shell get it right. Database Mail attachments, sqlcmd output and mysql --batch do not, and the file looks fine until the first customer name with a comma in it.

Encoding. Excel opens a UTF-8 CSV correctly only when the file starts with a byte order mark or the user imports it explicitly. Accented names turning into pairs of odd characters is the most common complaint about automated exports.

Credentials. Every script needs a database login and usually a mail password. Both end up in a file on a server, readable by whoever has access to that server.

Silence. When the job fails, the evidence is in a local log, a job history or a mail queue. The person waiting for the file notices first, usually days later.

When a scheduled export tool is the simpler answer

The homemade setup is the right call for one export owned by the engineer who wrote it. It stops being the right call when the exports are owned by people who think in SQL rather than in cron syntax, when there are several databases of different kinds, or when someone asks which reports went out this week and nobody can answer.

Taavik's scheduled exports cover the four parts in one place. An agent inside the network runs the query against PostgreSQL, MySQL, SQL Server, Azure SQL or Redshift and only connects outbound. The file is a quoted CSV with a header and a byte order mark, or an Excel file with a frozen header row. Schedules run daily, weekly, monthly, on an interval or on cron with the Pro plan, each in its own time zone. Delivery goes to an email list or a Slack channel, optionally only when rows came back, and each run leaves a record with its status and row count.

One difference from a script is intentional. The file is encrypted on the agent before upload, and recipients open it through a link with a passphrase instead of finding an attachment. For an extract with personal data that is the point. For a recipient who wants the spreadsheet in the inbox it is one extra click, and worth mentioning before switching.

The free plan includes one connection and one daily schedule, which is enough to retire the first script and decide about the rest.