mysqlで複数行にまたがるカラムをCSVフォーマットで出力する方法
地味なネタですが、今日はまってしまったのでメモっておきます。
とあるとmysqlのデータをselect * from xxx into outfileで外部ファイルに出力したいとします。
テーブルのスキーマは「URL、タイトル、 HTML本体」という構成だとしましょう。HTML本体の部分は改行がたくさん入っています。
このデータをあとからcsvとして正しく読み込めるようなフォーマットで出力したいわけです。
まずは普通に出力してみます。
select url, title, html from table into outfile "output.tsv";
そうするとこのような結果になります。
http://hoge.com タイトルですよ \
\
\〜途中省略〜\
mysql のinto outfileはデフォルトだとTAB区切りで、複数にまたがるデータについてはバックスラッシュでエスケープするようです。
区切り文字はなんでもいいんですが、改行している部分がこれだとCSVとしてはあつかえませんね。
例えばPerlでText::CSVで読み込むためには、複数行にまたがるカラムの場合、ダブルクオートで囲ってやる必要があります。
つまり上のサンプルだとこんな感じ
select url, title, html from table into outfile "output.csv" fields terminated by "," enclosed by '"';
"http://hoge.com","タイトルですよ",""
"
"
〜途中省略〜""
"
区切り文字をカンマにしてダブルクオートで囲むようにしました。
これならPerlのText::CSVとかで簡単にパースできそう!・・・と思ったらNG!!
ダブルクオートで囲った内部でダブルクオートがあるケースではmysqlが気を利かせてさらにダブルクオートでエスケープしてくれてるあたり、とてもいい感じなんですが、mysqlではメタ文字のエスケープとして改行の部分にも"が入ってしまいます。
これがよろしくない。
"" を1つのカラムだと認識してしまって、2行目以降でエラーになります。
なので囲み文字はダブルクオート、エスケープの文字をバックスラッシュにしてみます。
select url, title, html from table into outfile "output.csv" fields terminated by "," enclosed by '"' escaped by '\\';
そうするとこのような出力結果になります。
"http://hoge.com","タイトルですよ","\
\
\
〜途中省略〜\\
"
全てのカラムがダブルクオートで囲まれてますね。それと同時にエスケープはバックスラッシュになってます。
さてさて、Text::CSVでパースしてみます。こっち側でもちょっとした工夫があります。
use strict; use warnings; use Text::CSV_XS; use Data::Dumper; my $csv = Text::CSV_XS->new( { sep_char => ',', escape_char => "\\", allow_loose_escapes => 1, binary => 1 } ); open my $fh, "<", 'output.csv'; while ( my $row = $csv->getline($fh) ) { for (@$row) { print $_,"\n"; print "-" x 100,"\n"; } }
Text::CSV_XSのコンストラクタでsep_charやescape_charを指定してるのはまぁ普通なんですが、allow_loose_escapesをセットしてるところがミソです。これがないとエラーになります。
Text::CSV_XSのPODにはこんなことが書いてありました。
allow_loose_escapesBy default, parsing fields that have escape_char characters
that escape characters that do not need to be escaped, like:my $csv = Text::CSV_XS->new ({ escape_char => "\\" });
$csv->parse (qq{1,"my bar\'s",baz,42});would result in a parse error.
Though it is still bad practice to allow this format,
this option enables you to treat all escape character sequences equal.
ふーむ。
なんだか何言ってんだかいまいちよくわからんけど、結果としてはうまくできたので、まぁいいや。
それにしても地味というかレアケースというか、微妙なハウツーだね、これは。
だれか一人でも参考にする人がいれば、ミラクル!