1417

Is there an easy way to run a MySQL query from the Linux command line and output the results in CSV format?

Here's what I'm doing now:

mysql -u uid -ppwd -D dbname << EOQ | sed -e 's/        /,/g' | tee list.csv
select id, concat("\"",name,"\"") as name
from students
EOQ

It gets messy when there are a lot of columns that need to be surrounded by quotes, or if there are quotes in the results that need to be escaped.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MCS
  • 22,113
  • 20
  • 62
  • 76
  • 1
    You can use [`REPLACE()`](http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_replace) in your query to have the quotes escaped. – dsm Dec 10 '08 at 16:08
  • Look at my answer in [this stackoverflow][1] [1]: http://stackoverflow.com/questions/12242772/easiest-way-to-copy-a-table-from-one-database-to-another/29395487#29395487 – biniam Apr 01 '15 at 16:14
  • You can [Format Table Data as Text table](http://stackoverflow.com/a/34084279/1045444). – Somnath Muluk Dec 04 '15 at 09:45
  • 4
    The accepted answer to this stackoverflow question is probably the best way: http://stackoverflow.com/questions/3760631/mysql-delimiter-question – Samuel Åslund Jan 22 '16 at 16:28
  • I wrote a feature request on the MariaDB bug tracker (https://jira.mariadb.org/browse/MDEV-12879) You can vote on it. – Jared Beck May 23 '17 at 18:27
  • https://stackoverflow.com/questions/1119312/mysql-export-into-outfile-csv-escaping-chars – Sunil Rajput Jun 05 '17 at 11:27
  • Copied from a below deleted answer: `SELECT @@GLOBAL.secure_file_priv;` will show you the path you do have access to. I did not have write access to `/tmp/` but I did to `/var/lib/mysql-files/`. – Chloe Dec 19 '18 at 18:33
  • Possible answer if you do not have write access on the server: 1) Use mysqldump to dump the table. 2) Import the table into your local database. 3) Use accepted answer to export (`select ... into outfile`) into CSV. – Chloe Dec 19 '18 at 19:04
  • I am guessing you mean without programming code, right? – LongChalk Feb 13 '20 at 14:06
  • I often use https://tableconvert.com/mysql-to-csv instead of "INTO OUTPUT" – Raos Jan 11 '23 at 03:41

40 Answers40

2003

From Save MySQL query results into a text or CSV file:

SELECT order_id,product_name,qty
FROM orders
WHERE foo = 'bar'
INTO OUTFILE '/var/lib/mysql-files/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

Note: That syntax may need to be reordered to

SELECT order_id,product_name,qty
INTO OUTFILE '/var/lib/mysql-files/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM orders
WHERE foo = 'bar';

in more recent versions of MySQL.

Using this command, columns names will not be exported.

Also note that /var/lib/mysql-files/orders.csv will be on the server that is running MySQL. The user that the MySQL process is running under must have permissions to write to the directory chosen, or the command will fail.

If you want to write output to your local machine from a remote server (especially a hosted or virtualize machine such as Heroku or Amazon RDS), this solution is not suitable.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
  • 10
    You may need to be root. "The file is created on the server host, so you must have the FILE privilege to use this syntax." - https://dev.mysql.com/doc/refman/8.0/en/select-into.html – Curtis Yallop Jun 23 '21 at 21:10
  • Yes, it relies both on MySQL privileges and on write privileges to somewhere on the local filesystem. – cazort Jul 03 '21 at 17:12
  • `LINES TERMINATED BY '\n'` is not necessary, it's the default. Generally if you omit all the additional parameters you'll get a tab-delimited file where fields aren't enclosed. – mojuba Sep 24 '21 at 23:56
  • 1
    Regarding the path where to save the file, this is related to secure-priv-file directive. You can also disable it in my.ini and restarting the db or change that directory's permissions if you don't want (or can't) to use root. See https://stackoverflow.com/questions/32737478/how-should-i-tackle-secure-file-priv-in-mysql – Chris Jan 19 '22 at 08:44
  • I got the error: "The MySQL server is running with the --secure-file-priv option so it cannot execute this statement" – Flimm Feb 15 '23 at 14:53
  • before running the query execute `SHOW VARIABLES LIKE "secure_file_priv";` which will show (if any) which directory you can output to – brianlmerritt Feb 22 '23 at 16:27
  • 1
    For RDS if you're using Aurora mysql you can SELECT INTO OUTFILE S3 https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Integrating.SaveIntoS3.html – Carl Mar 23 '23 at 22:02
562
mysql your_database --password=foo < my_requests.sql > out.tsv

This produces a tab-separated format. If you are certain that commas do not appear in any of the column data (and neither do tabs), you can use this pipe command to get a true CSV (thanks to user John Carter):

... .sql | sed 's/\t/,/g' > out.csv
Ramesh R
  • 7,009
  • 4
  • 25
  • 38
Stan
  • 8,710
  • 2
  • 29
  • 31
  • 133
    the sed 'fix' does not compensate for commas that may appear in any of the selected data and will skew your columns outputted accordingly – Joey T Dec 11 '12 at 01:17
  • Everybody says it's not safe to spell your password on the command line. – Rodrigo Feb 16 '23 at 00:49
238

mysql --batch, -B

Print results using tab as the column separator, with each row on a new line. With this option, mysql does not use the history file. Batch mode results in non-tabular output format and escaping of special characters. Escaping may be disabled by using raw mode; see the description for the --raw option.

This will give you a tab-separated file. Since commas (or strings containing comma) are not escaped, it is not straightforward to change the delimiter to comma.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
serbaut
  • 5,852
  • 2
  • 28
  • 32
198

Here's a fairly gnarly way of doing it[1]:

mysql --user=wibble --password mydatabasename -B -e "select * from vehicle_categories;" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > vehicle_categories.csv

It works pretty well. Once again, though, a regular expression proves write-only.


Regex Explanation:

  • s/// means substitute what's between the first // with what's between the second //
  • the "g" at the end is a modifier that means "all instance, not just first"
  • ^ (in this context) means beginning of line
  • $ (in this context) means end of line

So, putting it all together:

s/'/\'/          Replace ' with \'
s/\t/\",\"/g     Replace all \t (tab) with ","
s/^/\"/          at the beginning of the line place a "
s/$/\"/          At the end of the line, place a "
s/\n//g          Replace all \n (newline) with nothing

[1] I found it somewhere and can't take any credit.

Tim Harding
  • 2,875
  • 1
  • 17
  • 10
  • There is a typo in the command: "--password=wobble" – avernus Jul 01 '22 at 07:50
  • 1
    @AydinÖzcan `--password` just prompts the user for a password after the command is executed. `wobble` is the database name but I agree this isn't clear and have edited it for clarity. Thanks. – Tim Harding Jul 07 '22 at 01:23
129

Pipe it through 'tr' (Unix/Cygwin only):

mysql <database> -e "<query here>" | tr '\t' ',' > data.csv

N.B.: This handles neither embedded commas, nor embedded tabs.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
strickli
  • 1,581
  • 1
  • 12
  • 7
  • 5
    I'm surprised at how less-upvoted this solution is. Currently the top solution requires a privilege that many db users don't have (and for good reason; it's a security risk for administrators to give it out.) Your solution here works without any special privileges, and also could probably be improved to address the shortcomings of commas or tabs, possibly with a substitution in the query itself. – cazort Jul 03 '21 at 17:09
  • 1
    Agree with the comments here, this is precisely what's needed to avoid mysql privilege hell. – gordon_freeman Aug 07 '22 at 22:45
83

This saved me a couple of times. It is fast and it works!

--batch Print results using tab as the column separator, with each row on a new line.

--raw disables character escaping (\n, \t, \0, and \)

Example:

mysql -udemo_user -p -h127.0.0.1 --port=3306 \
   --default-character-set=utf8mb4 --database=demo_database \
   --batch --raw < /tmp/demo_sql_query.sql > /tmp/demo_csv_export.tsv

For completeness you could convert to CSV (but be careful because tabs could be inside field values - e.g., text fields)

tr '\t' ',' < file.tsv > file.csv

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hrvoj3e
  • 2,512
  • 1
  • 23
  • 22
45

The OUTFILE solution given by Paul Tomblin causes a file to be written on the MySQL server itself, so this will work only if you have FILE access, as well as login access or other means for retrieving the file from that box.

If you don't have such access, and tab-delimited output is a reasonable substitute for CSV (e.g., if your end goal is to import to Excel), then serbaut's solution (using mysql --batch and optionally --raw) is the way to go.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Leland Woodbury
  • 559
  • 4
  • 4
42

MySQL Workbench can export recordsets to CSV, and it seems to handle commas in fields very well. The CSV opens up in OpenOffice Calc fine.

David Oliver
  • 2,424
  • 1
  • 24
  • 37
  • 3
    Thanks a million David. After spending 3 hours getting the newlines to output properly for HTML content in the data, I used the MySQL Workbench and in 2 minutes I had my CSV file ready. – mr-euro Jul 11 '12 at 17:02
  • I've just found it can save as XML, too, which is great. I'm hoping to migrate from one application to another by using XSLT to transform this XML into a CSV file suitable for importing into the target application. – David Oliver Aug 05 '12 at 21:27
  • mysql workbench is best option for import export feature. – cijagani Aug 05 '15 at 03:20
  • Well,but each time the workbench limit the select records up to 1000 and when it comes to much more records it does not work that well,the same condition for the import it often blocked if I try to import a relatively large csv file into the mysql database by workbench. – LancelotHolmes Oct 29 '16 at 06:42
  • 1
    I just successfully exported over half a million rows using mysql workbench so large files don't seem to be a problem. You just have to make sure you remove the select limit before running your query and you may also have to increase the following values in your my.ini file: max_allowed_packet = 500M, net_read_timeout = 600, net_write_timeout = 600 – Vincent Apr 22 '18 at 15:47
  • MySQL Workbench is not always an option, especially when the remote server prevents external connections for security purposes. – afilina Apr 11 '23 at 15:33
37

All of the solutions here to date, except the MySQL Workbench one, are incorrect and quite possibly unsafe (i.e., security issues) for at least some possible content in the MySQL database.

MySQL Workbench (and similarly phpMyAdmin) provide a formally correct solution, but they are designed for downloading the output to a user's location. They're not so useful for things like automating data export.

It is not possible to generate reliably correct CSV content from the output of mysql -B -e 'SELECT ...' because that cannot encode carriage returns and white space in fields. The '-s' flag to mysql does do backslash escaping, and might lead to a correct solution. However, using a scripting language (one with decent internal data structures that is, not Bash), and libraries where the encoding issues have already been carefully worked out is far safer.

I thought about writing a script for this, but as soon as I thought about what I'd call it, it occurred to me to search for preexisting work by the same name. While I haven't gone over it thoroughly, mysql2csv looks promising. Depending on your application, the YAML approach to specifying the SQL commands might or might not appeal though. I'm also not thrilled with the requirement for a more recent version of Ruby than comes as standard with my Ubuntu 12.04 (Precise Pangolin) laptop or Debian 6.0 (Squeeze) servers. Yes, I know I could use RVM, but I'd rather not maintain that for such a simple purpose.

mc0e
  • 2,699
  • 28
  • 25
  • 1
    You are right, for complex strings in the table you have to use some decent library, not just bash. I think nodejs has a better solutions for this kind of actions. like this [example](https://github.com/crzidea/mysql2csv/blob/master/index.js) – yeya Jul 28 '16 at 15:28
  • 1
    Hi, good answer, I would add a link to the python solution proposed below https://stackoverflow.com/a/35123787/1504300, which works well and is very simple. I tried to edit your post but the edit's been rejected – reallynice Jun 02 '17 at 12:44
  • Rob Miller's mysql2csv script insists on connecting to the database itself, either via network or socket, and cannot be used as a unix-style pipe. Maybe that's required but it really limits the use. – chrisinmtown Sep 09 '20 at 19:21
  • @chrisinmtown what would you want to pipe into it? Output of myslqldump maybe? As I've pointed out, the output of `mysql -B`can't be fixed. – mc0e Oct 04 '20 at 14:03
  • 1
    @mc0e I want to pipe *in* the output of mysqldump, read that as stdin. – chrisinmtown Oct 05 '20 at 13:20
  • That could be useful for exporting whole tables into csv, but it doesn't allow you to get at the output of queries, so you can't use much of the power of the mysql engine that way. – mc0e Oct 07 '20 at 04:00
34

Many of the answers on this page are weak, because they don't handle the general case of what can occur in CSV format. E.g., commas and quotes embedded in fields and other conditions that always come up eventually. We need a general solution that works for all valid CSV input data.

Here's a simple and strong solution in Python:

#!/usr/bin/env python

import csv
import sys

tab_in = csv.reader(sys.stdin, dialect=csv.excel_tab)
comma_out = csv.writer(sys.stdout, dialect=csv.excel)

for row in tab_in:
    comma_out.writerow(row)

Name that file tab2csv, put it on your path, give it execute permissions, then use it like this:

mysql OTHER_OPTIONS --batch --execute='select * from whatever;' | tab2csv > outfile.csv

The Python CSV-handling functions cover corner cases for CSV input format(s).

This could be improved to handle very large files via a streaming approach.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Johnson
  • 20,650
  • 6
  • 81
  • 80
  • 11
    An even more dependable solution would be to actually connect to the database with Python, then you should have an easier time doing what you need to do to deal with larger datasets (chunking results, streaming, etc). – Josh Rumbut Feb 04 '16 at 15:52
  • @JoshRumbut, really late, but I made https://stackoverflow.com/a/41840534/2958070 to complement your comment – Ben Aug 30 '19 at 16:28
  • Please see https://stackoverflow.com/questions/356578/how-to-output-mysql-query-results-in-csv-format/63831142#63831142 for an extended version of this script with an input dialect that handles embedded comma and double-quote characters! – chrisinmtown Sep 10 '20 at 13:51
  • 1
    What is the gist of it? Using a library? A built-in library? Can you [link to documentation](https://docs.python.org/3/library/csv.html), etc.? (But ***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today.). – Peter Mortensen Aug 06 '21 at 11:34
  • 1
    What would be required for a streaming approach? Can you elaborate a little bit in your answer? (But ***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today.) – Peter Mortensen Aug 12 '21 at 09:30
33

Use:

mysql your_database -p < my_requests.sql | awk '{print $1","$2}' > out.csv
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Steve
  • 363
  • 3
  • 2
  • 3
    I really like this one. It is much cleaner, and I like the use of awk. However, I would have probably gone with this: `mysql -uUser -pPassword your_database < my_requests.sql | awk 'BEGIN{OFS="=";} {print $1,$2}' > out.csv` – Josh Apr 11 '12 at 00:00
  • 10
    This fails for field values with spaces. – Barun Sharma Apr 10 '15 at 06:17
31

From your command line, you can do this:

mysql -h *hostname* -P *port number* --database=*database_name* -u *username* -p -e *your SQL query* | sed 's/\t/","/g;s/^/"/;s/$/"/;s/\n//g' > *output_file_name.csv*

Credits: Exporting table from Amazon RDS into a CSV file

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sri Murthy Upadhyayula
  • 22,620
  • 1
  • 18
  • 20
22

I encountered the same problem and Paul's Answer wasn't an option since it was Amazon RDS. Replacing the tab with the commas did not work as the data had embedded commas and tabs. I found that mycli, which is a drop-in alternative for the mysql-client, supports CSV output out of the box with the --csv flag:

mycli db_name --csv -e "select * from flowers" > flowers.csv
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shenal Silva
  • 1,955
  • 5
  • 26
  • 37
  • Works like a charm, you can install mycli in macOS via: `brew update && brew install mycli`. I'm never using the stock mysql client again, mycli is so legit! – bithavoc Jul 24 '20 at 02:40
  • 1
    Mycli and it's cousin pgcli are fantastic. I came here to add this tip mysql because I wasn't getting anywhere with the other solutions. Cleanly outputting to a local .csv is surprisingly difficult with mysql. – iturgeon Sep 04 '20 at 19:17
  • Awesome, it's so sad that this answer got very few "likes". It's the only solution that fits my needs. All other didn't work perfectly for me but this. I had line breaks in columns - and that led to new lines in Excel program when opening the file. – Skiff Nov 20 '20 at 23:57
  • This answer won't work if you're stuck using version 1.8.1 (maybe because you're using an older OS that still has Python 2.x), as '--csv' wasn't available in that version. – Joseph Van Riper Dec 14 '20 at 09:35
  • I'm puzzled as to why other solutions keep getting upvoted. Thank you for sharing info about mycli, it's now my default client. – RobMac Apr 29 '23 at 20:17
20

This answer uses Python and a popular third party library, PyMySQL. I'm adding it because Python's csv library is powerful enough to correctly handle many different flavors of .csv and no other answers are using Python code to interact with the database.

import contextlib
import csv
import datetime
import os

# https://github.com/PyMySQL/PyMySQL
import pymysql

SQL_QUERY = """
SELECT * FROM my_table WHERE my_attribute = 'my_attribute';
"""

# embedding passwords in code gets nasty when you use version control
# the environment is not much better, but this is an example
# https://stackoverflow.com/questions/12461484
SQL_USER = os.environ['SQL_USER']
SQL_PASS = os.environ['SQL_PASS']

connection = pymysql.connect(host='localhost',
                             user=SQL_USER,
                             password=SQL_PASS,
                             db='dbname')

with contextlib.closing(connection):
    with connection.cursor() as cursor:
        cursor.execute(SQL_QUERY)
        # Hope you have enough memory :)
        results = cursor.fetchall()

output_file = 'my_query-{}.csv'.format(datetime.datetime.today().strftime('%Y-%m-%d'))
with open(output_file, 'w', newline='') as csvfile:
    # http://stackoverflow.com/a/17725590/2958070 about lineterminator
    csv_writer = csv.writer(csvfile, lineterminator='\n')
    csv_writer.writerows(results)
Michael
  • 8,362
  • 6
  • 61
  • 88
Ben
  • 5,952
  • 4
  • 33
  • 44
  • An answer using python was already provided. http://stackoverflow.com/a/35123787/5470883 – Alexander Baltasar Jan 25 '17 at 09:39
  • 5
    @AlexanderBaltasar, right, and it looks useful, but it's not using Python code to interact with the database. See the comment on that on that question. – Ben Jan 26 '17 at 02:29
  • Re *"no other answers are using Python code to interact with the database."*: No, but [Chris Johnson's answer](https://stackoverflow.com/questions/356578/how-can-i-output-mysql-query-results-in-csv-format/35123787#35123787) also uses the 'csv' library. – Peter Mortensen Aug 06 '21 at 11:40
  • @PeterMortensen, the comments on that answer motivated me to write this answer :) – Ben Aug 06 '21 at 17:36
14

Also, if you're performing the query on the Bash command line, I believe the tr command can be used to substitute the default tabs to arbitrary delimiters.

$ echo "SELECT * FROM Table123" | mysql Database456 | tr "\t" ,
2240
  • 1,547
  • 2
  • 12
  • 30
  • This is a nice way to go if you're having trouble with file permissions, but it won't quote your fields, so other tools may misinterpret the CSV if your data includes commas or quotation marks. – Paul A Jungwirth Apr 08 '21 at 15:29
  • Requires -s (silent) option to produce tabbed output – JamesP Jul 19 '21 at 15:03
14

This is simple, and it works on anything without needing batch mode or output files:

select concat_ws(',',
    concat('"', replace(field1, '"', '""'), '"'),
    concat('"', replace(field2, '"', '""'), '"'),
    concat('"', replace(field3, '"', '""'), '"'))

from your_table where etc;

Explanation:

  1. Replace " with "" in each field --> replace(field1, '"', '""')
  2. Surround each result in quotation marks --> concat('"', result1, '"')
  3. Place a comma between each quoted result --> concat_ws(',', quoted1, quoted2, ...)

That's it!

Michael
  • 8,362
  • 6
  • 61
  • 88
Marty Hirsch
  • 191
  • 1
  • 3
  • 4
    Note that `NULL` values will be skipped by `concat_ws()`, resulting in a column mismatch. To avoid this, simply use an empty string instead: `IFNULL(field, '')` (or whatever else you use to represent `NULL`) – Marco Roy May 21 '19 at 23:11
11

You can have a MySQL table that uses the CSV engine.

Then you will have a file on your hard disk that will always be in a CSV format which you could just copy without processing it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jonathan
  • 2,183
  • 4
  • 20
  • 25
10

To expand on previous answers, the following one-liner exports a single table as a tab-separated file. It's suitable for automation, exporting the database every day or so.

mysql -B -D mydatabase -e 'select * from mytable'

Conveniently, we can use the same technique to list out MySQL's tables, and to describe the fields on a single table:

mysql -B -D mydatabase -e 'show tables'

mysql -B -D mydatabase -e 'desc users'

Field   Type    Null    Key Default Extra
id  int(11) NO  PRI NULL    auto_increment
email   varchar(128)    NO  UNI NULL    
lastName    varchar(100)    YES     NULL    
title   varchar(128)    YES UNI NULL    
userName    varchar(128)    YES UNI NULL    
firstName   varchar(100)    YES     NULL    
johntellsall
  • 14,394
  • 4
  • 46
  • 40
  • 4
    To convert to CSV: `mysql -B -D mydatabase -e 'select * from mytable' | sed -e 's/\t/,/g'` – DSimon Jan 12 '15 at 21:15
  • 7
    Using `sed -e 's/\t/,/g'` is only safe if you are sure that your data doesn't contain any commas or tabs. – awatts Oct 20 '15 at 16:23
  • @awatts I suggest: `sed -e 's/"/\\"/g' -e 's/\t/","/g' -e 's/^/"/' -e 's/$/"/'` – Flimm Feb 15 '23 at 15:07
8

Building on user7610, here is the best way to do it. With mysql outfile there were 60 mins of file ownership and overwriting problems.

It's not cool, but it worked in 5 mins.

php csvdump.php localhost root password database tablename > whatever-you-like.csv

<?php

$server = $argv[1];
$user = $argv[2];
$password = $argv[3];
$db = $argv[4];
$table = $argv[5];

mysql_connect($server, $user, $password) or die(mysql_error());
mysql_select_db($db) or die(mysql_error());

// fetch the data
$rows = mysql_query('SELECT * FROM ' . $table);
$rows || die(mysql_error());


// create a file pointer connected to the output stream
$output = fopen('php://output', 'w');

// output the column headings

$fields = [];
for($i = 0; $i < mysql_num_fields($rows); $i++) {
    $field_info = mysql_fetch_field($rows, $i);
    $fields[] = $field_info->name;
}
fputcsv($output, $fields);

// loop over the rows, outputting them
while ($row = mysql_fetch_assoc($rows)) fputcsv($output, $row);

?>
Michael Cole
  • 15,473
  • 7
  • 79
  • 96
8

Here's what I do:

echo $QUERY | \
  mysql -B  $MYSQL_OPTS | \
  perl -F"\t" -lane 'print join ",", map {s/"/""/g; /^[\d.]+$/ ? $_ : qq("$_")} @F ' | \
  mail -s 'report' person@address

The Perl script (snipped from elsewhere) does a nice job of converting the tab spaced fields to CSV.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
extraplanetary
  • 194
  • 1
  • 2
  • 2
    This is great. Slight improvement might be to quote everything except numbers `perl -F"\t" -lane 'print join ",", map {s/"/""/g; /^\d+(?:\.\d+)?$/ ? $_ : qq("$_")} @F '` -- yours would not quote `1.2.3` – artfulrobot Mar 15 '17 at 16:00
  • 1
    This should be the accepted solution IMHO, with @artfulrobot's improvement of course. – chrisinmtown Sep 25 '20 at 14:55
7

Not exactly as a CSV format, but the tee command from the MySQL client can be used to save the output into a local file:

tee foobar.txt
SELECT foo FROM bar;

You can disable it using notee.

The problem with SELECT … INTO OUTFILE …; is that it requires permission to write files at the server.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Denilson Sá Maia
  • 47,466
  • 33
  • 109
  • 111
  • If .csv extension is used instead of .txt, are there any formatting issues to be aware of? – datalifenyc May 08 '18 at 21:53
  • @myidealab Formatting issues arise from commas etc. not being escaped. CSV is a plain text format so there's no formatting issue just from swapping out the extension. – jkmartindale Jun 20 '19 at 17:24
6

In my case from table_name ..... before INTO OUTFILE ..... gives an error:

Unexpected ordering of clauses. (near "FROM" at position 10)

What works for me:

SELECT *
INTO OUTFILE '/Volumes/Development/sql/sql/enabled_contacts.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM table_name
WHERE column_name = 'value'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Md. Robi Ullah
  • 1,703
  • 3
  • 20
  • 32
  • This is what I was looking for, but be aware that many modern MySQL servers are set up running --secure-file-priv which could prevent this from working – SteJ Jul 15 '22 at 11:56
5

What worked for me:

SELECT *
FROM students
WHERE foo = 'bar'
LIMIT 0,1200000
INTO OUTFILE './students-1200000.csv'
FIELDS TERMINATED BY ',' ESCAPED BY '"'
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n';

None of the solutions on this thread worked for my particular case. I had pretty JSON data inside one of the columns, which would get messed up in my CSV output. For those with a similar problem, try lines terminated by \r\n instead.

Also another problem for those trying to open the CSV with Microsoft Excel, keep in mind there is a limit of 32,767 characters that a single cell can hold, above that it overflows to the rows below. To identify which records in a column have the issue, use the query below. You can then truncate those records or handle them as you'd like.

SELECT id,name,CHAR_LENGTH(json_student_description) AS 'character length'
FROM students
WHERE CHAR_LENGTH(json_student_description)>32767;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rohit Chemburkar
  • 1,019
  • 8
  • 13
3

Using the solution posted by Tim Harding, I created this Bash script to facilitate the process (root password is requested, but you can modify the script easily to ask for any other user):

#!/bin/bash

if [ "$1" == "" ];then
    echo "Usage: $0 DATABASE TABLE [MYSQL EXTRA COMMANDS]"
    exit
fi

DBNAME=$1
TABLE=$2
FNAME=$1.$2.csv
MCOMM=$3

echo "MySQL password: "
stty -echo
read PASS
stty echo

mysql -uroot -p$PASS $MCOMM $DBNAME -B -e "SELECT * FROM $TABLE;" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > $FNAME

It will create a file named: database.table.csv

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lepe
  • 24,677
  • 9
  • 99
  • 108
  • what about embedded double-quote characters? They need to be escaped also but I don't see a pattern in the sed incantation for them. – chrisinmtown Sep 25 '20 at 14:50
3

If you have PHP set up on the server, you can use mysql2csv to export an (actually valid) CSV file for an arbitrary MySQL query. See my answer at MySQL - SELECT * INTO OUTFILE LOCAL ? for a little more context/info.

I tried to maintain the option names from mysql so it should be sufficient to provide the --file and --query options:

./mysql2csv --file="/tmp/result.csv" --query='SELECT 1 as foo, 2 as bar;' --user="username" --password="password"

"Install" mysql2csv via

wget https://gist.githubusercontent.com/paslandau/37bf787eab1b84fc7ae679d1823cf401/raw/29a48bb0a43f6750858e1ddec054d3552f3cbc45/mysql2csv -O mysql2csv -q && (sha256sum mysql2csv | cmp <(echo "b109535b29733bd596ecc8608e008732e617e97906f119c66dd7cf6ab2865a65  mysql2csv") || (echo "ERROR comparing hash, Found:" ;sha256sum mysql2csv) ) && chmod +x mysql2csv

(Download content of the gist, check checksum and make it executable.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hirnhamster
  • 7,101
  • 8
  • 43
  • 73
3

The following produces tab-delimited and valid CSV output. Unlike most of the other answers, this technique correctly handles escaping of tabs, commas, quotes, and new lines without any stream filter like sed, AWK, or tr.

The example shows how to pipe a remote MySQL table directly into a local SQLite database using streams. This works without FILE permission or SELECT INTO OUTFILE permission. I have added new lines for readability.

mysql -B -C --raw -u 'username' --password='password' --host='hostname' 'databasename'
-e 'SELECT
    CONCAT('\''"'\'',REPLACE(`id`,'\''"'\'', '\''""'\''),'\''"'\'') AS '\''id'\'',
    CONCAT('\''"'\'',REPLACE(`value`,'\''"'\'', '\''""'\''),'\''"'\'') AS '\''value'\''
    FROM sampledata'
2>/dev/null | sqlite3 -csv -separator $'\t' mydb.db '.import /dev/stdin mycsvtable'

The 2>/dev/null is needed to suppress the warning about the password on the command line.

If your data has NULLs, you can use the IFNULL() function in the query.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
humbads
  • 3,252
  • 1
  • 27
  • 22
3

A simple solution in Python that writes a standard-format CSV file with headers and writes data as a stream (low memory use):

import csv

def export_table(connection, table_name, output_filename):
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM " + table_name)

    # thanks to https://gist.github.com/madan712/f27ac3b703a541abbcd63871a4a56636 for this hint
    header = [descriptor[0] for descriptor in cursor.description]

    with open(output_filename, 'w') as csvfile:
        csv_writer = csv.writer(csvfile, dialect='excel')
        csv_writer.writerow(header)
        for row in cursor:
            csv_writer.writerow(row)

You could use it like:

import mysql.connector as mysql
# (or https://github.com/PyMySQL/PyMySQL should work but I haven't tested it)

db = mysql.connect(
    host="localhost",
    user="USERNAME",
    db="DATABASE_NAME",
    port=9999)

for table_name in ['table1', 'table2']:
    export_table(db, table_name, table_name + '.csv')

db.close()

For simplicity, this intentionally doesn't include some fancier stuff from another answer like using an environment variable for credentials, contextlib, etc. There is a subtlety mentioned there about line endings that I haven't evaluated.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ken Arnold
  • 1,942
  • 1
  • 20
  • 24
2

Tiny Bash script for doing simple query to CSV dumps, inspired by Tim Harding's answer.

#!/bin/bash

# $1 = query to execute
# $2 = outfile
# $3 = mysql database name
# $4 = mysql username

if [ -z "$1" ]; then
    echo "Query not given"
    exit 1
fi

if [ -z "$2" ]; then
    echo "Outfile not given"
    exit 1
fi

MYSQL_DB=""
MYSQL_USER="root"

if [ ! -z "$3" ]; then
    MYSQL_DB=$3
fi

if [ ! -z "$4" ]; then
    MYSQL_USER=$4
fi

if [ -z "$MYSQL_DB" ]; then
    echo "Database name not given"
    exit 1
fi

if [ -z "$MYSQL_USER" ]; then
    echo "Database user not given"
    exit 1
fi

mysql -u $MYSQL_USER -p -D $MYSQL_DB -B -s -e "$1" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > $2
echo "Written to $2"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
minitauros
  • 1,920
  • 18
  • 20
  • 1
    This uses the sed incantation that ignores embedded double-quote characters. Suggest using the perl solution instead. – chrisinmtown Sep 25 '20 at 14:52
2

If you are getting an error of secure-file-priv then, also after shifting your destination file location inside the C:\ProgramData\MySQL\MySQL Server 8.0\Uploads and also after then the query -

SELECT * FROM attendance INTO OUTFILE 'C:\ProgramData\MySQL\MySQL Server 8.0\Uploads\FileName.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';

is not working, you have to just change \(backsplash) from the query to / (forwardsplash)

And that works!!

Example:

SELECT * FROM attendance INTO OUTFILE 'C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/FileName.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';

Each time when you run the successful query, it will generate the new CSV file each time! Cool, right?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AAYUSH SHAH
  • 131
  • 2
  • 6
1

The following Bash script works for me. It optionally also gets the schema for the requested tables.

#!/bin/bash
#
# Export MySQL data to CSV
#https://stackoverflow.com/questions/356578/how-to-output-mysql-query-results-in-csv-format
#

# ANSI colors
#http://www.csc.uvic.ca/~sae/seng265/fall04/tips/s265s047-tips/bash-using-colors.html
blue='\033[0;34m'
red='\033[0;31m'
green='\033[0;32m' # '\e[1;32m' is too bright for white bg.
endColor='\033[0m'

#
# A colored message
#   params:
#     1: l_color - the color of the message
#     2: l_msg - the message to display
#
color_msg() {
  local l_color="$1"
  local l_msg="$2"
  echo -e "${l_color}$l_msg${endColor}"
}


#
# Error
#
# Show the given error message on standard error and exit
#
#   Parameters:
#     1: l_msg - the error message to display
#
error() {
  local l_msg="$1"
  # Use ANSI red for error
  color_msg $red "Error:" 1>&2
  color_msg $red "\t$l_msg" 1>&2
  usage
}

#
# Display usage
#
usage() {
  echo "usage: $0 [-h|--help]" 1>&2
  echo "               -o  | --output      csvdirectory"    1>&2
  echo "               -d  | --database    database"   1>&2
  echo "               -t  | --tables      tables"     1>&2
  echo "               -p  | --password    password"   1>&2
  echo "               -u  | --user        user"       1>&2
  echo "               -hs | --host        host"       1>&2
  echo "               -gs | --get-schema"             1>&2
  echo "" 1>&2
  echo "     output: output CSV directory to export MySQL data into" 1>&2
  echo "" 1>&2
  echo "         user: MySQL user" 1>&2
  echo "     password: MySQL password" 1>&2
  echo "" 1>&2
  echo "     database: target database" 1>&2
  echo "       tables: tables to export" 1>&2
  echo "         host: host of target database" 1>&2
  echo "" 1>&2
  echo "  -h|--help: show help" 1>&2
  exit 1
}

#
# show help
#
help() {
  echo "$0 Help" 1>&2
  echo "===========" 1>&2
  echo "$0 exports a CSV file from a MySQL database optionally limiting to a list of tables" 1>&2
  echo "   example: $0 --database=cms --user=scott --password=tiger  --tables=person --output person.csv" 1>&2
  echo "" 1>&2
  usage
}

domysql() {
  mysql --host $host -u$user --password=$password $database
}

getcolumns() {
  local l_table="$1"
  echo "describe $l_table" | domysql | cut -f1 | grep -v "Field" | grep -v "Warning" | paste -sd "," - 2>/dev/null
}

host="localhost"
mysqlfiles="/var/lib/mysql-files/"

# Parse command line options
while true; do
  #echo "option $1"
  case "$1" in
    # Options without arguments
    -h|--help) usage;;
    -d|--database)     database="$2" ; shift ;;
    -t|--tables)       tables="$2" ; shift ;;
    -o|--output)       csvoutput="$2" ; shift ;;
    -u|--user)         user="$2" ; shift ;;
    -hs|--host)        host="$2" ; shift ;;
    -p|--password)     password="$2" ; shift ;;
    -gs|--get-schema)  option="getschema";;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; usage;;
    (*) break;;
  esac
  shift
done

# Checks
if [ "$csvoutput" == "" ]
then
  error "output CSV directory is not set"
fi
if [ "$database" == "" ]
then
  error "MySQL database is not set"
fi
if [ "$user" == "" ]
then
  error "MySQL user is not set"
fi
if [ "$password" == "" ]
then
  error "MySQL password is not set"
fi

color_msg $blue "exporting tables of database $database"
if [ "$tables" = "" ]
then
tables=$(echo "show tables" | domysql)
fi

case $option in
  getschema)
   rm $csvoutput$database.schema
   for table in $tables
   do
     color_msg $blue "getting schema for $table"
     echo -n "$table:" >> $csvoutput$database.schema
     getcolumns $table >> $csvoutput$database.schema
   done
   ;;
  *)
for table in $tables
do
  color_msg $blue "exporting table $table"
  cols=$(grep "$table:" $csvoutput$database.schema | cut -f2 -d:)
  if [  "$cols" = "" ]
  then
    cols=$(getcolumns $table)
  fi
  ssh $host rm $mysqlfiles/$table.csv
cat <<EOF | mysql --host $host -u$user --password=$password $database
SELECT $cols FROM $table INTO OUTFILE '$mysqlfiles$table.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
EOF
  scp $host:$mysqlfiles/$table.csv $csvoutput$table.csv.raw
  (echo "$cols"; cat $csvoutput$table.csv.raw) > $csvoutput$table.csv
  rm $csvoutput$table.csv.raw
done
  ;;
esac
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186
1

Standing on the shoulders of Chris Johnson, I extended the answer from Feb 2016 with a custom dialect for reading.

This shell pipeline tool does not need to connect to your database, handles random commas and quotes in the input, and works nicely in Python 2 and Python 3!

#!/usr/bin/env python
import csv
import sys

# fields are separated by tabs; double-quotes may occur anywhere
csv.register_dialect("mysql", delimiter="\t", quoting=csv.QUOTE_NONE)
tab_in = csv.reader(sys.stdin, dialect="mysql")
comma_out = csv.writer(sys.stdout, dialect=csv.excel)
for row in tab_in:
    # print("row: {}".format(row))
    comma_out.writerow(row)

Use that print statement to convince yourself it's parsing your input correctly :)

A major caveat: treatment of carriage return characters, ^M aka control-M, \r in Linux terms. Although batch-mode MySQL output correctly escapes embedded newline characters, so there is truly one row per line (defined by the Linux newline character \n), MySQL puts no quotes around column data. If a data item has an embedded carriage-return character, csv.reader rejects that input with this exception:

new-line character seen in unquoted field -
do you need to open the file in universal-newline mode?

Please don't @ me saying I should use universal file mode by re-opening sys.stdin.fileno with mode 'rU'. I tried that, and it causes the embedded \r characters to be treated as end-of-record markers, so a single input record is incorrectly transformed into many incomplete output records.

I have not found a Python solution to this limitation of Python's csv.reader module. I think the root cause is the csv.reader implementation/limitation noted in their documentation, csv.reader:

The reader is hard-coded to recognise either '\r' or '\n' as end-of-line,
and ignores lineterminator.

The weak and unsatisfying solution I can offer is to change each \r character to the two-character sequence '\n' before Python's csv.reader sees the data. I used the sed command. Here's an example of a pipeline with a MySQL select and the Python script from above:

mysql -u user db --execute="select * from table where id=12345" \
  | sed -e 's/\r/\\n/g' \
  | mysqlTsvToCsv.py

After fighting this for some time I think Python is not the right solution. If you can live with Perl, I think the one-liner script offered by artfulrobot may be the most-effective and simplest solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chrisinmtown
  • 3,571
  • 3
  • 34
  • 43
1

This one avoids having to write output to a file, only requires expat to be installed, properly escapes values, and outputs empty string (instead of a literal NULL) for null values.

You tell MySQL to output the results in XML format (using the --xml flag), and then pipe the results through the C program below.

This should also be pretty close to the fastest possible way to do this.


// mysql-xml-to-csv.c

#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <expat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/*
    Example of MySQL XML output:

    <?xml version="1.0"?>

    <resultset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" statement="SELECT id as IdNum, lastName, firstName FROM User">
        <row>
            <field name="IdNum">100040</field>
            <field name="lastName" xsi:nil="true"/>
            <field name="firsttName">Cher</field>
        </row>
    </resultset>
*/

#define BUFFER_SIZE     (1 << 16)

// These accumulate the first row column names and values until first row is entirely read (unless the "-N" flag is given)
static XML_Char **column_names;
static size_t num_column_names;
static XML_Char **first_row_values;
static size_t num_first_row_values;

// This accumulates one column's value
static XML_Char *elem_text;                     // note: not nul-terminated
static size_t elem_text_len;

// Flags
static int first_column;
static int reading_value;

// Expat callback functions
static void handle_elem_start(void *data, const XML_Char *el, const XML_Char **attr);
static void handle_elem_text(void *userData, const XML_Char *s, int len);
static void handle_elem_end(void *data, const XML_Char *el);

// Helper functions
static void output_csv_row(XML_Char **values, size_t num);
static void output_csv_text(const char *s, size_t len);
static void add_string(XML_Char ***arrayp, size_t *lengthp, const XML_Char *string, size_t len);
static void add_chars(XML_Char **strp, size_t *lenp, const XML_Char *string, size_t nchars);
static size_t xml_strlen(const XML_Char *string);
static void free_strings(XML_Char ***arrayp, size_t *lengthp);
static void usage(void);

int
main(int argc, char **argv)
{
    char buf[BUFFER_SIZE];
    int want_column_names = 1;
    XML_Parser p;
    FILE *fp;
    size_t r;
    int i;

    // Parse command line
    while ((i = getopt(argc, argv, "hN")) != -1) {
        switch (i) {
        case 'N':
            want_column_names = 0;
            break;
        case 'h':
            usage();
            exit(0);
        case '?':
        default:
            usage();
            exit(1);
        }
    }
    argv += optind;
    argc -= optind;
    switch (argc) {
    case 0:
        fp = stdin;
        break;
    case 1:
        if ((fp = fopen(argv[0], "r")) == NULL)
            err(1, "%s", argv[0]);
        break;
    default:
        usage();
        exit(1);
    }

    // Initialize arrays for column names and first row values
    if (want_column_names) {
        if ((column_names = malloc(10 * sizeof(*column_names))) == NULL)
            err(1, "malloc");
        if ((first_row_values = malloc(10 * sizeof(*first_row_values))) == NULL)
            err(1, "malloc");
    }

    // Initialize parser
    if ((p = XML_ParserCreate(NULL)) == NULL)
        errx(1, "can't initialize parser");
    XML_SetElementHandler(p, handle_elem_start, handle_elem_end);
    XML_SetCharacterDataHandler(p, handle_elem_text);

    // Process file
    while (1) {
        if ((r = fread(buf, 1, sizeof(buf), fp)) == 0 && ferror(fp))
            errx(1, "error reading input");
        if (XML_Parse(p, buf, r, r == 0) == XML_STATUS_ERROR)
            errx(1, "line %u: %s", (unsigned int)XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
        if (r == 0)
            break;
    }

    // Clean up
    XML_ParserFree(p);
    fclose(fp);

    // Done
    return 0;
}

static void
handle_elem_start(void *data, const XML_Char *name, const XML_Char **attr)
{
    if (strcmp(name, "row") == 0)
        first_column = 1;
    else if (strcmp(name, "field") == 0) {
        if (column_names != NULL) {
            while (*attr != NULL && strcmp(*attr, "name") != 0)
                attr += 2;
            if (*attr == NULL)
                errx(1, "\"field\" element is missing \"name\" attribute");
            add_string(&column_names, &num_column_names, attr[1], xml_strlen(attr[1]));
        } else {
            if (!first_column)
                putchar(',');
            putchar('"');
        }
        reading_value = 1;
    }
}

static void
handle_elem_text(void *userData, const XML_Char *s, int len)
{
    if (!reading_value)
        return;
    if (column_names != NULL)
        add_chars(&elem_text, &elem_text_len, s, len);
    else
        output_csv_text(s, len);
}

static void
handle_elem_end(void *data, const XML_Char *name)
{
    if (strcmp(name, "row") == 0) {
        if (column_names != NULL) {
            output_csv_row(column_names, num_column_names);
            output_csv_row(first_row_values, num_first_row_values);
            free_strings(&column_names, &num_column_names);
            free_strings(&first_row_values, &num_first_row_values);
        } else
            putchar('\n');
    } else if (strcmp(name, "field") == 0) {
        if (column_names != NULL) {
            add_string(&first_row_values, &num_first_row_values, elem_text, elem_text_len);
            free(elem_text);
            elem_text = NULL;
            elem_text_len = 0;
        } else
            putchar('"');
        first_column = 0;
        reading_value = 0;
    }
}

static void
output_csv_row(XML_Char **values, size_t num_columns)
{
    int i;

    for (i = 0; i < num_columns; i++) {
        if (i > 0)
            putchar(',');
        putchar('"');
        output_csv_text(values[i], xml_strlen(values[i]));
        putchar('"');
    }
    putchar('\n');
}

static void
output_csv_text(const XML_Char *s, size_t len)
{
    while (len-- > 0) {
        if (*s == '"')
            putchar('"');
        putchar(*s);
        s++;
    }
}

static void
add_string(XML_Char ***arrayp, size_t *lengthp, const XML_Char *string, size_t nchars)
{
    char **new_array;

    if ((new_array = realloc(*arrayp, (*lengthp + 1) * sizeof(**arrayp))) == NULL)
        err(1, "malloc");
    *arrayp = new_array;
    if (((*arrayp)[*lengthp] = malloc((nchars + 1) * sizeof(XML_Char))) == NULL)
        err(1, "malloc");
    memcpy((*arrayp)[*lengthp], string, nchars * sizeof(XML_Char));
    (*arrayp)[*lengthp][nchars] = (XML_Char)0;
    (*lengthp)++;
}

static void
add_chars(XML_Char **strp, size_t *lenp, const XML_Char *string, size_t nchars)
{
    XML_Char *new_array;

    if ((new_array = realloc(*strp, (*lenp + nchars) * sizeof(XML_Char))) == NULL)
        err(1, "malloc");
    *strp = new_array;
    memcpy(*strp + *lenp, string, nchars * sizeof(XML_Char));
    *lenp += nchars;
}

static size_t
xml_strlen(const XML_Char *string)
{
    size_t len;

    len = 0;
    while (string[len] != (XML_Char)0)
        len++;
    return len;
}

static void
free_strings(char ***arrayp, size_t *lengthp)
{
    while (*lengthp > 0)
        free((*arrayp)[--*lengthp]);
    free(*arrayp);
    *arrayp = NULL;
}

static void
usage(void)
{
    fprintf(stderr, "Usage: mysql-xml-to-csv [options] [file.xml]\n");
    fprintf(stderr, "Options:\n");
    fprintf(stderr, "  -N\tDo not output column names as the first row\n");
    fprintf(stderr, "  -h\tShow this usage info\n");
}

For those who don't work with C very often, you can build this code by running the following (assuming you have the expat library installed):

gcc mysql-xml-to-csv.c -lexpat -o mysql-xml-to-csv

Tested with openSUSE 15.2 and gcc 7.5.0.

Update: Now available as an open source project on github.

Archie
  • 4,959
  • 1
  • 30
  • 36
  • What platform was this tested on? Operating system (incl. version), GCC version, etc. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/67007702/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Aug 06 '21 at 12:38
1

This is dirty and ugly. It is only suitable for the particular situation where all you have is a PHP-*-admin and the server is running with the --secure-file-priv option, so then you cannot use the INTO OUTFILE '/path/to/export.csv' clause in your query.

What you can do is parse lines of CSV with ... wait for it!, CONCAT, then copy the results and paste them into a file.

Here's an example, where I needed SQL format (it'd be trivial to adapt it for CSV):

SELECT CONCAT(
"('",
`username`, "', '",
`password`, "', '",
`first_name`, "', '",
`last_name`, "', '",
`gender`, "'),"
) AS `row` 
FROM `users`
WHERE `role` != 'Not set'
AND `user_status` = 'Active'
ORDER BY `role`, `gender`, `user_id`
LIMIT 200

That gives nice, ready for import output similar to this:

('jane', '3d7ff...', 'Jane', 'Doe', 'Female'),  
('john', 'd2a33...', 'John', 'Doe', 'Male'),
...
Majid Fouladpour
  • 29,356
  • 21
  • 76
  • 127
0

Try this code:

SELECT 'Column1', 'Column2', 'Column3', 'Column4', 'Column5'
UNION ALL
SELECT column1, column2,
column3 , column4, column5 FROM demo
INTO OUTFILE '/tmp/demo.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

For more information: http://dev.mysql.com/doc/refman/5.1/en/select-into.html

trungduc
  • 11,926
  • 4
  • 31
  • 55
Indrajeet Singh
  • 2,958
  • 25
  • 25
  • Regard same if i run following query i am getting error. My query : https://justpaste.it/2fp6f Any help thanks. @Indrajeet Singh – zus Feb 19 '20 at 05:34
  • An explanation would be in order. E.g., what is the idea/gist? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/25723906/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Aug 06 '21 at 11:12
0

This solution places the SQL query in a heredoc and pipes the output though a filter:

File query.sh

#!/bin/bash

mysql --defaults-group-suffix=[DATABASE_NAME] --batch << EOF | python query.py
SELECT [FIELDS]
FROM [TABLE]
EOF

This version of the Python filter works without requiring the use of the csv module:

File query.sh

import sys

for line in sys.stdin:
    print(','.join(["\"" + str(element) + "\"" for element in line.rstrip('\n').split('\t')]))

This version of the Python filter uses the CSV module and involves slightly more code but is arguably a little bit more clear:

File query.sh

import csv, sys

csv_reader = csv.reader(sys.stdin, delimiter='\t')
csv_writer = csv.writer(sys.stdout, quoting=csv.QUOTE_NONNUMERIC)

for line in csv_reader:
    csv_writer.writerow(line)

Or you could use Pandas:

File query.py

import csv, sys
import pandas as pd

df = pd.read_csv(sys.stdin, sep='\t')
df.to_csv(sys.stdout, index=False, quoting=csv.QUOTE_NONNUMERIC)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alex Ryan
  • 3,719
  • 5
  • 25
  • 41
0

For those, who may want to download a query result in CSV format, but doesn't have access the server file but the database.

First of all, it's not a Linux command. Steps are bellow:

  1. Create a view with the query. For example: (Create VIEW v as (Select * from user where status = 0))
  2. The view will be created under the view section of your database.
  3. Now export the view as CSV.
  4. If you need the table column as header of CSV, set Export method: to Custom - display all possible options and check Put columns names in the first row.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sudarshan
  • 938
  • 14
  • 27
0

If you are getting this error while you try to export your file

ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

and you are not able to solve this error, you can do one thing by simply running this Python script

import mysql.connector
import csv

con = mysql.connector.connect(
    host="localhost",
    user="root",
    passwd="Your Password"
)

cur = con.cursor()

cur.execute("USE DbName")
cur.execute("""
select col1,col2 from table
where <cond>
""")

with open('Filename.csv',mode='w') as data:
    fieldnames=["Field1","Field2"]
    writer=csv.DictWriter(data,fieldnames=fieldnames)
    writer.writeheader()
    for i in cur:
        writer.writerow({'Field1':i[0],'Field2':i[1]})
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pranav
  • 105
  • 5
  • Welcome to Stack Overflow! This doesn't answer the question. Please review https://stackoverflow.com/help/how-to-answer. – stephenwade Apr 04 '20 at 16:25
0

If there is PHP installed on the machine you are using, you can write a PHP script to do that. It requires the PHP installation has the MySQL extension installed.

You can call the PHP interpreter from the command line like so:

php --php-ini path/to/php.ini your-script.php

I am including the --php-ini switch, because you may need to use your own PHP configuration that enables the MySQL extension. On PHP 5.3.0+ that extension is enabled by default, so that is no longer necessary to use the configuration to enable it.

Then you can write your export script like any normal PHP script:

<?php
    #mysql_connect("localhost", "username", "password") or die(mysql_error());
    mysql_select_db("mydb") or die(mysql_error());

    $result = mysql_query("SELECT * FROM table_with_the_data p WHERE p.type = $typeiwant");

    $result || die(mysql_error());

    while($row = mysql_fetch_row($result)) {
      $comma = false;
      foreach ($row as $item) {

        # Make it comma separated
        if ($comma) {
          echo ',';
        } else {
          $comma = true;
        }

        # Quote the quotes
        $quoted = str_replace("\"", "\"\"", $item);

        # Quote the string
        echo "\"$quoted\"";
      }
        echo "\n";
    }
?>

The advantage of this method is, that it has no problems with varchar and text fields, that have text containing newlines. Those fields are correctly quoted and those newlines in them will be interpreted by the CSV reader as a part of the text, not record separators. That is something that is hard to correct afterwards with sed or so.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user7610
  • 25,267
  • 15
  • 124
  • 150
  • 1
    Far from being unnecessarily complicated, this is the only solution here that goes in a direction likely to be correct - albeit with a bit more work. CSV is more complex than it first appears, and you've made a variety of mistakes. eg backslashes in the original. Better to use a library which has worked out all the issues for writing to CSV. – mc0e Oct 10 '13 at 08:57
  • @mc0e Usually you either double the quote or escape the quote. I decided to double the quote, therefore I do not need an escape character. Different software has different ideas about CSV details. For example, LOAD DATA in MySQL indeed treats \ as escape character, whereas Open Office Calc does not. When I wrote the answer I was exporting data into a spreadsheet. – user7610 Nov 02 '15 at 12:51
  • 1
    My code handles everything that was needed to export _my_ dataset back in the day ;) Your answer is also a step in the correct direction, but as the first comment there says, it should 1) connect to the sql database from the script directly as well as 2) use a proper csv library. – user7610 Oct 22 '18 at 09:04
-3

If you are on production or any other server with no access to a file system, you can use this simple trick and a little bit of manual effort to get what you want.

Step 1. Just wrap all the columns under CONCAT and use as CSVFormat option provided by MySQL to get comma-delimited results (or use any delimiter you want). Here is an example:

SELECT
    CONCAT(u.id,
            ',',
            given,
            ',',
            family,
            ',',
            email,
            ',',
            phone,
            ',',
            ua.street_number,
            ',',
            ua.route,
            ',',
            ua.locality,
            ',',
            ua.state,
            ',',
            ua.country,
            ',',
            ua.latitude,
            ',',
            ua.longitude) AS CSVFormat
FROM
    table1 u
        LEFT JOIN
    table2 ua ON u.address_id = ua.id
WHERE
    role_policy = 31 and is_active = 1;

Step 2. Copy results from your terminal to a file and clean up all the pipe characters (that forms the layout of your results) using any text editor.

Step 3. Save as .csv file and that's it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
saran3h
  • 12,353
  • 4
  • 42
  • 54
-4

You can use the below command from your SQL editor/Terminal:

mysql -h(hostname/IP>) -u(username) -p(password) databasename <(query.sql) > outputFILE(.txt/.xls)

E.g.,

  • hostname -x.x.x.x

  • uname - username

  • password - password

  • DBName - employeeDB

  • queryFile - employee.sql

  • outputFile - outputFile.xls

mysql -hx.x.x.x -uusername -ppassword employeeDB< employee.sql> outputFile.xls

Make sure you are executing the command from the directory where SQL query is located or mention the full path of the SQL query location in the above command.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ripudaman Singh
  • 363
  • 3
  • 10