ナクナイ

勉強用の備忘録

[mysql] INTO OUTFILE 構文以外でクエリ結果を CSV 出力

mysql でクエリ結果を CSV に出力したい場面に出くわしました。
一応、下記のようなクエリを発行すれば出力は可能です。

SELECT
'カラム名1',
'カラム名2',
'カラム名3',
...
FROM テーブル名
UNION 
SELECT *
INTO OUTFILE '保存したいパス'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY "\n"
FROM テーブル名
WHERE 1

それぞれ下記の様な意味です。

INTO OUTFILE: 出力先パス
FIELDS TERMINATED BY: 区切り文字
OPTIONALLY ENCLOSED BY: 囲い文字
LINES TERMINATED BY: 行が変わる際の文字

UNION 句以前の SELECT は、カラム名を1行目に挿入するために入れています。ちょっとスマートじゃない感じ。

このクエリだと UNION の関係上件数が多くなると激重になってしまいますし、mysql が稼動しているサーバーにファイルが生成されることになるので、ローカル環境に移動したいときとかに面倒です。ローカル環境に mysql コマンドがあれば、下記のように実行することで移動する手間もかからないしクエリも重くなりにくく、上記と同じ結果が得ることが出来ます。

$ /usr/local/mysql/bin/mysql -h ホスト名 -u ユーザー名 -pパスワード DB名 -e "SELECT * FROM テーブル名 WHERE 1" | awk '{gsub("\"","\\\""); $0="\""$0"\""; gsub("\t", "\",\""); print $0}' > '出力先'

awk はあまり使ったことないので調べながらやってみました。ひとつひとつの処理を説明するとこんな感じ。

gsub("\"","\\\"");

区切り文字を " にするので、まずは値に " が含まれるものをあらかじめエスケープしておく。

$0="\""$0"\"";

次に、行頭と行末に " を加える。

gsub("\t", "\",\"");

最後に、タブ文字を "," に置換。


クエリは軽くなったので mysql への負荷は減りましたが、当然ファイル出力や awk などの負荷は、実行するローカル環境にかかるので留意が必要です。とはいえ、mysql よりローカル環境に負荷をかけたほうがベターなケースがほとんどだと思います。

ただ、ちょっと awk にがんばらせすぎかなとも思っていて、たとえば SELECT で取得してきた際に始めから " 付きでデータを取ってこれたり出来ないかなーとか思い、
文字列を扱う関数:MySQL関数リファレンス
↑ここらへんを調べたのですがなかなかいい方法は見つからず。。なにかもっとスマートな方法があれば教えてください。


ちなみに、最初は下記のように awk ではなく sed でやろうとしてたんですが、タブ文字置換のところで正しく動作しなかったので、awk に切り替えました。

$ /usr/local/mysql/bin/mysql -h ホスト名 -u ユーザー名 -pパスワード DB名 -e "SELECT * FROM テーブル名 WHERE 1" | sed -e 's/\t/,/g

あまり深追いはしてませんが、2 バイト文字あたりでこける場合があったような気がしないでもない・・・



おわり