Migrate data from Google BigQuery tables into a MySQL database using EXPORT DATA and LOAD DATA INFILE commands.
Teams move analytical datasets from BigQuery to MySQL when they need OLTP-style serving, cheaper storage, or to embed data in application workflows. MySQL’s widespread hosting options and ACID compliance make it attractive for production workloads.
First export tables from BigQuery to Cloud Storage. Second copy the files to the MySQL host. Third create equivalent MySQL schemas. Fourth bulk-load the data with LOAD DATA INFILE or mysqlimport. Finally verify counts and automate incremental syncs.
EXPORT DATA OPTIONS(
uri='gs://my-bucket/customers-*.csv',
format='CSV',
overwrite=true,
header=true
) AS
SELECT * FROM `project.dataset.Customers`;
This command writes sharded CSVs with headers to Cloud Storage. Use one export per table.
Use gsutil or Storage Transfer Service to pull files locally, e.g. gsutil cp gs://my-bucket/customers-*.csv /tmp/
. Ensure the MySQL user running LOAD DATA has read permission on the directory.
CREATE TABLE Customers(
id BIGINT PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255),
created_at DATETIME
) ENGINE=InnoDB;
Match column order and types to the exported CSV to avoid casting errors.
LOAD DATA INFILE '/tmp/customers-000000000000.csv'
INTO TABLE Customers
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(id, name, email, @created)
SET created_at = STR_TO_DATE(@created,'%Y-%m-%d %H:%i:%s');
Sharded CSVs can be loaded in parallel sessions. Disable secondary indexes during bulk load for speed.
Run SELECT COUNT(*) FROM Customers;
in MySQL and compare with bq query 'SELECT COUNT(*) FROM project.dataset.Customers'
. Mismatches mean truncation or encoding issues.
Wrap EXPORT and LOAD commands in a CI job. Use checksum tables or CDC tools like Airbyte to sync daily deltas after the initial full load.
Export gzip-compressed CSV to cut network time. Align time zones to UTC. Convert BigQuery TIMESTAMP to MySQL DATETIME with explicit formatting. Keep a rollback backup of the target database.
NUMERIC has higher precision; cast it to DECIMAL(38,9) in MySQL to prevent overflow.
Headers become data, shifting columns. Always include IGNORE 1 ROWS when your export adds headers.
Check Google’s EXPORT DATA docs and MySQL’s LOAD DATA INFILE guide for advanced options.
No native streaming path exists. You must export to Cloud Storage or use a data-integration tool that performs extract-load.
GZIP-compressed CSV balances speed and size. MySQL decompresses on the fly, and network transfer is smaller.
Use incremental exports based on updated_at columns or deploy a CDC tool like Airbyte or Debezium.