PerlでSQL接続/操作を学ぶための自作ドリル
DBI経由でPostgreSQLに接続し、データの登録/参照を行う。
問題
CSVファイルの内容をDBに登録する
DBとテーブルはPgAdmin3で用意する。
CSVファイル(sample.csv)
name,email,address "山田 太郎",taro.yamada@xxx.com,"東京" "田中 次郎",jiro.tanaka@xxx.com,"大阪" "阿部 三郎",saburo.abe@xxx.com,"名古屋"
実行結果
$ insert_sql.pl sample.csv
DB登録内容(テーブル)
name | address | |
---|---|---|
山田 太郎 | taro.yamada@xxx.com | 東京 |
田中 次郎 | jiro.tanaka@xxx.com | 大阪 |
阿部 三郎 | saburo.abe@xxx.com | 名古屋 |
DBの内容をXMLファイルに書き出す
DB登録内容(テーブル)
name | address | |
---|---|---|
山田 太郎 | taro.yamada@xxx.com | 東京 |
田中 次郎 | jiro.tanaka@xxx.com | 大阪 |
阿部 三郎 | saburo.abe@xxx.com | 名古屋 |
実行結果
$ select_sql.pl sample_out.xml
<?xml version="1.0" encoding="UTF-8"?> <addressBook> <person> <name>山田 太郎</name> <email>taro.yamada@xxx.com</email> <address>東京</address> </person> <person> <name>田中 次郎</name> <email>jiro.tanaka@xxx.com</email> <address>大阪</address> </person> <person> <name>阿部 三郎</name> <email>saburo.abe@xxx.com</email> <address>名古屋</address> </person> </addressBook>
解答例
CSVファイルの内容をDBに登録する
データベース設定
DB名 | testdb |
---|---|
テーブル名 | addr_book |
ホスト名 | localhost(デフォルト) |
ポート番号 | 5432(デフォルト) |
ユーザー名 | postgres(デフォルト) |
パスワード | postgres |
テーブル設定
列名 | データ型 | 制約 |
---|---|---|
name | character varying | primary key, unique, not null |
character varying | not null | |
address | character varying | not null |
use strict; use warnings; use utf8; use DBI; use Text::CSV_XS; binmode STDIN, ':utf8'; binmode STDOUT, ':utf8'; binmode STDERR, ':utf8'; # CSV Parser my $csv = Text::CSV_XS->new({binary=>1}); # Input Check if (@ARGV != 1) { print STDERR "Please input CSV file name.\n"; exit 1; } # 読み込みファイル名 my $csvin_name = $ARGV[0]; # File Check unless (-f $csvin_name) { print STDERR "$csvin_name is not found.\n"; exit 1; } # Input CSV open my $csvin, '<:utf8', $csvin_name; my @table = (); while (my $line = $csv->getline($csvin)) { push @table, $line; } close $csvin; # DBI Connect my $DB = DBI->connect("DBI:Pg:dbname=testdb;host=localhost;port=5432", 'postgres', 'postgres', {RaiseError => 1, AutoCommit => 1}); # Insert Datas my @labels = @{(shift @table)}; foreach my $row (@table) { my $sqltext = sprintf('insert into addr_book (%s) values (%s);', join (',', @labels), '\'' . join ('\',\'', @$row) . '\''); my $st = $DB->prepare($sqltext) or die $DB->errStr; $st->execute(); } $DB->disconnect(); exit 0;
DBの内容をXMLファイルに書き出す
use strict; use warnings; use utf8; use DBI; use XML::LibXML; binmode STDIN, ':utf8'; binmode STDOUT, ':utf8'; binmode STDERR, ':utf8'; # 書き出し先ファイル名 my $xmlout_name = $ARGV[0] || 'output_' . time . '.xml'; # File Check if (-e $xmlout_name) { print STDERR "$xmlout_name already exists.\n"; exit 1; } # DBI Connect my $DB = DBI->connect("DBI:Pg:dbname=testdb;host=localhost;port=5432", 'postgres', 'postgres', {RaiseError => 1, AutoCommit => 1}); # Select Datas my $sqltext = 'select * from addr_book'; my $st = $DB->prepare($sqltext) or die $DB->errStr; $st->execute(); my $labels = $st->{NAME}; my $table = $st->fetchall_arrayref; $DB->disconnect(); # XML output my $xml = XML::LibXML::Document->new('1.0', 'UTF-8'); my $adb = XML::LibXML::Element->new('addressBook'); $xml->addChild($adb); foreach my $row (@$table) { my $node = XML::LibXML::Element->new('person'); for (my $cnt = 0; $cnt < @$labels; $cnt++) { my $data = XML::LibXML::Element->new($labels->[$cnt]); $data->appendText($row->[$cnt]); $node->addChild($data); } $adb->addChild($node); } # File Double-check if (-e $xmlout_name) { print STDERR "$xmlout_name already exists.\n"; exit 1; } # XML Output $xml->toFile($xmlout_name, 1); exit 0;
追記
この問題は、DB接続処理よりも環境設定のほうに手間取る事が分かった。
DBD::Pgの導入に気を付ければ、後は比較的楽だろう。