読者です 読者をやめる 読者になる 読者になる

NeverBlog::Likk::Unexistable;

見なかったことにして下さい

OracleのSQL出力をMysqlライクに

対話式武藤 - マツモブログ
対話式+Text::ASCIITable あぁ、うんこれは便利そうということで、SQLを投げるやつ作ってみた。

作った背景として、sqlplusの結果がぐちゃっとしてて見辛いという理由から。
現在、Oracle ClientやMS Exchangeその他GUIが使えない環境で、sshOracleサーバに接続して、sqlplusコマンド打って、SQL文を直接叩く。という地味な作業やっております。

環境や設定にも因るとは思うのですが、次のテーブルにsqlplusを投げるとこんな感じ

M_USER
ID NAME REG_DATE UPD_DATE DEL_FLG
1 HOGE 2007-01-01 00:00:00 2007-04-01 12:00:00 1
2 FUGA 2007-03-07 17:54:33 2007-03-07 17:54:33 0
select * from M_USER;

       ID NAME                          REG_DATE
---------- ------------------------------ -------------------
UPD_DATE            DEL_FLG
------------------- -------
        1  HOGE                           2007-01-01 00:00:00
2007-04-01 12:00:00 1

        2  FUGA                           2007-03-07 17:54:33
2007-03-07 17:54:33 0

別に慣れれば平気なのかも知れんが、mysqlだとこう。

+----+------+---------------------+---------------------+---------+
| ID | NAME | REG_DATE            | UPD_DATE            | DEL_FLG |
+----+------+---------------------+---------------------+---------+
|  1 | HOGE | 2007-01-01 00:00:00 | 2007-04-01 12:00:00 |       1 |
|  2 | FUGA | 2007-03-07 17:54:33 | 2007-03-07 17:54:33 |       0 |
+----+------+---------------------+---------------------+---------+

どっちが見易いかって、そりゃ後者だ。(ワシだけか?)
select ID||','||NAME||','||REG_DATE||','||……… なselect句にして回避していたが、これも縦位置が揃わない。

ということで、対話式SQL&整形ツールをトラバ元参照に作った。

use strict;
use warnings;
use DBI;
use Term::ReadLine;
use Text::ASCIITable;

my $output = "./.sqlhistory";
open (my $fh,">>$output");
my $term = Term::ReadLine->new('SQL');
my $prompt = "SQL : ";
my $OUT = $term->OUT || *STDOUT;
$ENV{ORACLE_HOME}=q|/path/to/oracle_home/|;
$ENV{NLS_LANG} = q|American_Japan.JA16SJIS|;
our $dbh = DBI::connect('dbi:Oracle',user,password);

our $sql = "";
while (defined(my $in = $term->readline($prompt))) {
	if(!defined $in or $in eq ''){
		next;
	}elsif ($in eq 'exit' or $in eq 'quit') {
		$dbh->disconnect();
		print "Bye!!\n";
		last;
	}else{
		if($in !~ m/;$/){
			$sql .= "$in\n";
			next;
		}else{
			$in =~ s/;$//;		
			$sql .= $in;
		}
		my ($result) = sqlexec($dbh,$sql);
		$sql ="";
		if(!defined $result->[0] or $result->[0] ==0){
			print "SQL Error => $result->[1]\n";
		}else{
			my $header = $result->[1];
			my $data_ref = $result->[2];
			my $t = Text::ASCIITable->new();
			$t->setCols(@$header);
			for my $line_ref (@$data_ref) {
				my @row = ();
				for my $key(@$header){
					push @row,$line_ref->{$key}
				}
        		$t->addRow(@row);
			}
			print $t;
			print $fh "$sql\n";
			print $fh $t;
		}		
    }
	$term->addhistory($_) if /\S/;
}
close $fh;

sub sqlexec{
	my $dbh = shift;
	my $sql_data = shift;
	my $sth = $dbh->prepare($sql_data) or return [0,$dbh->err.":".$dbh->errstr];	
	$sth->execute() or return[0,$sth->err.":".$sth->errstr];
	my @tbl_header = ();
	my @line_result = ();
	for (0..$sth->{NUM_OF_FIELDS}-1){
		push @tbl_header,$sth->{NAME}->[$_];
	}
	while(my $data_ref = $sth->fetchrow_hashref()){
		push @line_result,$data_ref;
	}
	return [1,\@tbl_header,\@line_result];
}

結果はこんな感じ

ohama@cndbss ~ > $HOME/local/bin/sqltbl.pl
SQL : select *
SQL :         from M_USER
SQL : ;
.------------------------------------------------------------------.
| ID | NAME  | REG_DATE            | UPD_DATE            | DEL_FLG |
+----+-------+---------------------+---------------------+---------+
|  1 | HOGE  | 2007-01-01 00:00:00 | 2007-04-01 12:00:00 |       1 |
|  2 | FUGA  | 2007-03-07 17:54:33 | 2007-03-07 17:54:33 |       0 |
'----+-------+---------------------+---------------------+---------'

ついでに、実行結果と発行したSQL文の履歴を実行したディレクトリに 
.sqlhistory というファイル名で保存します。(名前は適当)

補足:select文にしか対応してない。updateやinsert文もexcuteするとは思いますが、未検証。DDLはもっと責任持てません。describeとか対応してない。