Existe-t-il un moyen simple d'exécuter une requête MySQL à partir de la ligne de commande Linux et d'afficher les résultats au format CSV ?
Voici ce que je fais maintenant:
mysql -u uid -ppwd -D dbname << EOQ | sed -e 's/ /,/g' | tee list.csv
select id, concat("\"",name,"\"") as name
from students
EOQ
Cela devient compliqué lorsqu'il y a beaucoup de colonnes qui doivent être entourées de guillemets ou s'il y a des guillemets dans les résultats qui doivent être échappés.
De http://www.tech-recipes.com/rx/1475/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';
En utilisant cette commande, les noms de colonnes ne seront pas exportés.
Notez également que /var/lib/mysql-files/orders.csv
sera sur le serveur qui exécute MySQL. L'utilisateur sous lequel le processus MySQL s'exécute doit disposer des autorisations nécessaires pour écrire dans le répertoire choisi, sinon la commande échouera.
Si vous souhaitez écrire la sortie sur votre ordinateur local à partir d'un serveur distant (notamment une machine hébergée ou virtualisée telle que Heroku ou Amazon RDS), cette solution ne convient pas.
$ mysql your_database --password=foo < my_requests.sql > out.csv
Qui est séparé par une tabulation. Pipe-le comme ça pour obtenir un vrai CSV (merci @therefromhere):
... .sql | sed 's/\t/,/g' > out.csv
mysql --batch, -B
Imprimez les résultats en utilisant la tabulation comme séparateur de colonne, avec chaque ligne sur un nouvelle ligne. Avec cette option, mysql n'utilise pas le fichier d'historique . Le mode batch produit un format de sortie non tabulaire et échappe à caractères spéciaux. Échapper peut être désactivé en utilisant le mode brut; voir la description de l'option --raw.
Cela vous donnera un fichier séparé par des tabulations. Comme les virgules (ou les chaînes contenant des virgules) ne sont pas omises, il n'est pas simple de changer le délimiteur en virgule.
Voici une façon assez géniale de le faire. Je l'ai trouvé quelque part, je ne peux prendre aucun crédit
mysql --user=wibble --password wobble -B -e "select * from vehicle_categories;" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > vehicle_categories.csv
Fonctionne assez bien. Encore une fois, une expression rationnelle ne prouve que l'écriture.
Explication Regex:
Alors, tout mettre ensemble:
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
Unix / Cygwin only, dirigez-le à travers 'tr':
mysql <database> -e "<query here>" | tr '\t' ',' > data.csv
NB: Cela ne gère ni les virgules incorporées, ni les onglets incorporés.
La solution OUTFILE proposée par Paul Tomblin provoque l’écriture d’un fichier sur le serveur MySQL lui-même. Elle ne fonctionnera donc que si vous disposez du fichier FILE , ainsi que d’un identifiant ou autre moyen de récupération du fichier à partir de boîte.
Si vous ne disposez pas de cet accès et que la sortie délimitée par des tabulations constitue un substitut raisonnable du fichier CSV (par exemple, si votre objectif final est d'importer dans Excel), la solution de Serbaut (utilisant mysql --batch
et éventuellement --raw
) est la solution.
MySQL Workbench peut exporter des jeux d’enregistrements au format CSV, et il semble très bien gérer les virgules dans les champs. Le fichier CSV s'ouvre dans OpenOffice.
Que diriez-vous:
mysql your_database -p < my_requests.sql | awk '{print $1","$2}' > out.csv
À ce jour, toutes les solutions, à l'exception de la base de travail Mysql, sont incorrectes et tout à fait potentiellement dangereuses (problèmes de sécurité, par exemple) pour au moins un contenu possible dans la base de données mysql.
Mysql Workbench (et de la même manière PHPMyAdmin) fournit une solution formellement correcte, mais est conçu pour télécharger la sortie vers l'emplacement d'un utilisateur. Ils ne sont pas très utiles pour l'automatisation de l'exportation des données.
Il n’est pas possible de générer de manière fiable un fichier csv correct à partir de la sortie de mysql -B -e 'SELECT ...'
car cela ne permet pas d’encoder les retours chariot et les espaces dans les champs. Le drapeau '-s' à mysql fait une barre oblique inverse, et peut conduire à une solution correcte. Cependant, il est beaucoup plus sûr d'utiliser un langage de script (un langage avec des structures de données internes correctes, et non pas bash), et des bibliothèques dans lesquelles les problèmes de codage ont déjà été soigneusement élaborés.
J'ai pensé écrire un script pour cela, mais dès que j'ai pensé à comment je l'appellerais, il m'est apparu de rechercher des travaux préexistants du même nom. Bien que je ne l'aie pas examinée en détail, la solution à https://github.com/robmiller/mysql2csv semble prometteuse. En fonction de votre application, l’approche yaml de spécification des commandes SQL pourrait ou non intéresser. Je ne suis pas non plus enthousiasmé par l'exigence d'une version plus récente de Ruby par rapport à celle standard de mon ordinateur portable Ubuntu 12.04 ou de mes serveurs Debian Squeeze. Oui, je sais que je pourrais utiliser RVM, mais je préférerais ne pas le conserver pour un but aussi simple.
J'espère que quelqu'un indiquera un outil approprié, qui a fait l'objet de quelques essais. Sinon, je mettrai probablement ceci à jour si j'en trouve ou en écris un.
Depuis votre ligne de commande, vous pouvez faire ceci:
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:Exportation d'une table à partir d'Amazon RDS dans un fichier csv
Beaucoup de réponses sur cette page sont faibles car elles ne gèrent pas le cas général de ce qui peut se produire au format CSV. par exemple. des virgules et des guillemets incorporés dans les champs et d'autres conditions qui finissent toujours par se présenter. Nous avons besoin d'une solution générale qui fonctionne pour toutes les données d'entrée CSV valides.
Voici une solution simple et puissante en 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)
Nommez ce fichier tab2csv
, mettez-le sur votre chemin, donnez-lui les autorisations d'exécution, puis utilisez-le avec la liste suivante:
mysql OTHER_OPTIONS --batch --execute='select * from whatever;' | tab2csv >outfile.csv
Les fonctions de gestion Python CSV couvrent les cas de figure pour les formats d’entrée CSV.
Cela pourrait être amélioré pour gérer des fichiers très volumineux via une approche de streaming.
Alternativement à la réponse ci-dessus, vous pouvez avoir une table MySQL qui utilise le moteur CSV.
Ensuite, vous aurez un fichier sur votre disque dur qui sera toujours au format CSV que vous pourrez simplement copier sans le traiter.
C'est simple et cela fonctionne sur n'importe quoi sans avoir besoin du mode batch ou des fichiers de sortie:
select concat_ws(',',
concat('"', replace(field1, '"', '""'), '"'),
concat('"', replace(field2, '"', '""'), '"'),
concat('"', replace(field3, '"', '""'), '"'))
from your_table where etc;
Explication:
C'est tout!
Cette réponse utilise Python et une bibliothèque tierce populaire, PyMySQL . Je l'ajoute car la bibliothèque csv de Python est assez puissante pour correctement gérer de nombreux types de .csv
et aucune autre réponse n'utilise le code Python pour interagir avec la base de données.
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)
Pour développer les réponses précédentes, la ligne suivante exporte un tableau unique en tant que fichier séparé par des tabulations. Il convient à l’automatisation, à l’exportation de la base de données tous les jours.
mysql -B -D mydatabase -e 'select * from mytable'
De manière pratique, nous pouvons utiliser la même technique pour lister les tables de MySQL et décrire les champs d’une seule 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
De plus, si vous exécutez la requête sur la ligne de commande Bash, je pense que la commande tr
peut être utilisée pour substituer les onglets par défaut à des délimiteurs arbitraires.
$ echo "SELECT * FROM Table123" | mysql Database456 | tr "\t" ,
En s'appuyant sur user7610, voici la meilleure façon de le faire. Avec mysql outfile
, il y a eu 60 minutes de problèmes de propriété de fichier et de remplacement.
Ce n'est pas cool, mais cela a fonctionné en 5 minutes.
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);
?>
Pas exactement comme un format CSV, mais tee
commande de MySQL client peut être utilisé pour enregistrer le résultat dans un fichier local:
tee foobar.txt
SELECT foo FROM bar;
Vous pouvez le désactiver en utilisant notee
.
Le problème avec SELECT … INTO OUTFILE …;
est qu'il nécessite l'autorisation d'écrire des fichiers sur le serveur.
Voici ce que je fais:
echo $QUERY | \
mysql -B $MYSQL_OPTS | \
Perl -F"\t" -lane 'print join ",", map {s/"/""/g; /^[\d.]+$/ ? $_ : qq("$_")} @F ' | \
mail -s 'report' person@address
Le script Perl (tiré d’ailleurs) fait un travail agréable en convertissant les champs espacés en onglets au format CSV.
En utilisant la solution publiée par Tim, j'ai créé ce script bash pour faciliter le processus (un mot de passe root est demandé, mais vous pouvez le modifier facilement pour demander à tout autre utilisateur):
#!/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
Il créera un fichier nommé: database.table.csv
Si PHP est configuré sur le serveur, vous pouvez utiliser mysql2csv pour exporter un fichier CSV (réellement valide) pour une requête mysql aléatoire. Voir ma réponse à MySQL - SELECT * INTO OUTFILE LOCAL? pour un peu plus de contexte/info.
J'ai essayé de conserver les noms d'options de mysql
et il devrait donc être suffisant de fournir les options --file
et --query
:
./mysql2csv --file="/tmp/result.csv" --query='SELECT 1 as foo, 2 as bar;' --user="username" --password="password"
"Installer" 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
(téléchargez le contenu du Gist, vérifiez la somme de contrôle et rendez-le exécutable).
Essayez ce 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';
Pour plus d'informations: http://dev.mysql.com/doc/refman/5.1/fr/select-into.html
Petit script bash pour effectuer une requête simple sur les vidages CSV, inspiré par https://stackoverflow.com/a/5395421/2841607 .
#!/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"
Le script bash suivant fonctionne pour moi. Il obtient également éventuellement le schéma pour les tables demandées.
#!/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 stderr and exit
#
# params:
# 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 "ouput csv directory not set"
fi
if [ "$database" == "" ]
then
error "mysql database not set"
fi
if [ "$user" == "" ]
then
error "mysql user not set"
fi
if [ "$password" == "" ]
then
error "mysql password 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
Ce qui suit produit délimité par des tabulations et valide Sortie CSV. Contrairement à la plupart des autres réponses, cette technique gère correctement l'échappement d'onglets, de virgules, de guillemets et de nouvelles lignes sans aucun filtre de flux tel que sed, awk ou tr. L'exemple montre comment diriger une table mysql distante directement vers une base de données sqlite locale à l'aide de flux. Cela fonctionne sans l’autorisation FILE ou l’autorisation SELECT INTO OUTFILE. J'ai ajouté de nouvelles lignes pour la lisibilité.
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'
Le 2>/dev/null
est nécessaire pour supprimer l'avertissement concernant le mot de passe sur la ligne de commande.
Si vos données contiennent des valeurs NULL, vous pouvez utiliser la fonction IFNULL () dans la requête.
Si PHP est installé sur la machine que vous utilisez, vous pouvez écrire un script PHP pour le faire. L'extension MySQL est requise pour l'installation de PHP.
Vous pouvez appeler l'interpréteur PHP à partir de la ligne de commande comme suit:
php --php-ini path/to/php.ini your-script.php
J'inclus le commutateur --php-ini
, car vous devrez peut-être utiliser votre propre configuration PHP qui active l'extension MySQL. Sur PHP 5.3.0+, cette extension est activée par défaut, il n’est donc plus nécessaire d’utiliser la configuration pour l’activer.
Ensuite, vous pouvez écrire votre script d'exportation comme n'importe quel script PHP normal:
<?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";
}
?>
L'avantage de cette méthode est qu'elle ne présente aucun problème avec les champs varchar et text, qui contiennent du texte contenant des nouvelles lignes. Ces champs sont correctement cités et leurs nouvelles lignes seront interprétées par le lecteur CSV comme faisant partie du texte et non comme des séparateurs d’enregistrements. C’est quelque chose qui est difficile à corriger après avec sed ou presque.
Ce qui a fonctionné pour moi:
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';
Aucune des solutions sur ce fil ne fonctionnait pour mon cas particulier, j'avais de jolies données json dans l'une des colonnes, ce qui risquerait d'être gâché par ma sortie csv. Pour les personnes ayant un problème similaire, essayez plutôt les lignes terminées par\r\n.
Autre problème également pour ceux qui tentent d’ouvrir le fichier csv avec Microsoft Excel, n’oubliez pas qu’une seule cellule peut contenir 32 767 caractères, mais qu’elle déborde au-delà des lignes. Pour identifier les enregistrements de la colonne présentant le problème, utilisez la requête ci-dessous. Vous pouvez ensuite tronquer ces enregistrements ou les traiter comme vous le souhaitez.
SELECT id,name,CHAR_LENGTH(json_student_description) AS 'character length'
FROM students
WHERE CHAR_LENGTH(json_student_description)>32767;