テーブル定義のexcelファイル出力(1)
2007/09/17
2011/05/26
テーブル定義って考えることが多くて大変だし、excel上だけで、作業するのも退屈なので、以前から実際のスキーマ情報からテーブル定義を自動生成したいとは思っていました。DBDesignerというソフトウェアで、実際のDBからテーブル定義をリバースエンジニアリングしてから、カラム名の日本語説明やら備考やらを書き込んで、HTMLリポートで出力ということも試してみたんですが、満足できませんでした。
最近Postgresqlを使うことが多いんですが、postgresqlでは、DBのオブジェクトにCOMMENTを付加することができるので、それを利用して日本語名やら備考を設定していけば、実際のDBからテーブル定義書を出力できるようにできます。
仕様
汚い仕様ですが、例えばカラムに対してのコメントは、「カラム名;備考」というように、;で区切った前がカラム名で;以降が備考というように設定することにしました。pgadmin3というツールを使えばコメントを付加するのは簡単です。できたらMySQLとかも対応できたらいいけど、必要にならないとやらないかも。(MySQLにコメントを付加する機能があるかどうかも知りません。)
実装
リハビリの意味も含めてperlで書きました。
DbDefInfo.pm | DBD:PgPP で各テーブル、カラム情報をコメント付きでとってきます。 |
---|---|
WriteExcelDbDefInfo.pm | Spreadsheet::WriteExcelで、excelファイルを出力します。 |
create_table_def.pl | 実行のエントリポイント |
途中で疲れてきたので、SQLとか変数名とかバグとかPODがないとか不快なものがあると思います。見付けたら直していきます。恥を忍んで貼り付けます。
DbDefInfo.pm
2007/09/18 SQL文が間違っているので後で修正しました。
package DbDefInfo; use strict; use warnings; our($VERSION, @ISA, @EXPORT, @EXPORT_OK); require Exporter; use Carp; $VERSION = '0.0001'; @ISA = qw(Exporter); @EXPORT = qw(get_table_def_infos); @EXPORT_OK = qw(); use DBI; my $column_info_query = <<EOD; SELECT DISTINCT PG_ATTRIBUTE.ATTNAME AS NAME, PG_TYPE.TYPNAME AS TYPE, PG_ATTRIBUTE.ATTTYPMOD - 4 AS SUBTYPE, PG_ATTRIBUTE.ATTNOTNULL AS NOTNULL, PG_ATTRDEF.ADSRC AS DEFAULT, PG_DESCRIPTION.DESCRIPTION AS DESCRIPTION, PG_ATTRIBUTE.ATTNUM AS NO, PG_ATTRIBUTE.ATTALIGN FROM PG_ATTRIBUTE INNER JOIN PG_CLASS ON PG_CLASS.OID = PG_ATTRIBUTE.ATTRELID INNER JOIN PG_TYPE ON PG_ATTRIBUTE.ATTTYPID = PG_TYPE.OID LEFT JOIN PG_DESCRIPTION ON PG_CLASS.OID = PG_DESCRIPTION.OBJOID AND PG_ATTRIBUTE.ATTNUM = PG_DESCRIPTION.OBJSUBID LEFT JOIN PG_ATTRDEF ON PG_ATTRIBUTE.ATTRELID = PG_ATTRDEF.ADRELID AND PG_ATTRIBUTE.ATTNUM = PG_ATTRDEF.ADNUM WHERE PG_CLASS.RELNAME='%s' AND PG_ATTRIBUTE.ATTNUM > 0 ORDER BY PG_ATTRIBUTE.ATTNUM EOD my $keys_info_query = <<EOD; SELECT C.RELNAME AS TABLENAME, A.ATTNAME AS COLUMNNAME, FCLASS.RELNAME AS FTABLENAME, FATTR.ATTNAME AS FCOLUMNNAME FROM PG_CONSTRAINT N INNER JOIN PG_CLASS C ON N.CONRELID = C.OID INNER JOIN PG_ATTRIBUTE A ON A.ATTRELID = C.OID LEFT JOIN PG_CLASS AS FCLASS ON N.CONFRELID = FCLASS.OID LEFT JOIN PG_ATTRIBUTE FATTR ON FATTR.ATTRELID = C.OID AND FATTR.ATTNUM = ANY(N.CONFKEY) WHERE A.ATTNUM =ANY (N.CONKEY) AND C.RELNAME = '%s' EOD my $all_table_query = <<EOD; SELECT DISTINCT PG_CLASS.RELNAME, PG_DESCRIPTION.DESCRIPTION FROM PG_CLASS INNER JOIN PG_TABLES ON PG_CLASS.RELNAME = PG_TABLES.TABLENAME LEFT JOIN PG_DESCRIPTION ON PG_CLASS.OID = PG_DESCRIPTION.OBJOID AND PG_DESCRIPTION.OBJSUBID = 0 WHERE SCHEMANAME = '%s' AND PG_CLASS.RELKIND = 'r' EOD sub get_all_tables { my ($db, $schema_name) = @_; my $st = $db->prepare(sprintf $all_table_query, $schema_name); my $res = $st->execute; my @table_infos; while (my ($table_name, $table_comment) = $st->fetchrow) { push @table_infos, {table_name => $table_name, table_comment => $table_comment}; } $st->finish; return @table_infos; } sub get_keys_infos { my ($db, $table_name) = @_; my @keys; my $st_keys = $db->prepare(sprintf($keys_info_query, $table_name)); my $res_columns = $st_keys->execute; while (my $row_hashref = $st_keys->fetchrow_hashref) { push @keys, $row_hashref; } $st_keys->finish; return @keys; } sub get_column_infos { my ($db, $table_name) = @_; my @columns; my $st_columns = $db->prepare(sprintf($column_info_query, $table_name)); my $res_columns = $st_columns->execute; while (my $row_hashref = $st_columns->fetchrow_hashref) { push @columns, $row_hashref; } $st_columns->finish; return @columns; } sub get_table_def_infos { my $settings = shift; my $db = DBI->connect( "DBI:$settings->{type}:host=$settings->{host};dbname=$settings->{dbname}", $settings->{user}, $settings->{passwd}) or die "DBI connect failed : $DBI::errstr"; my @rows; for my $table_info (get_all_tables($db, $settings->{schema_name})) { my @columns = get_column_infos($db, $table_info->{table_name}); my @keys = get_keys_infos($db, $table_info->{table_name}); push @rows, { table_info => $table_info, column_infos => \@columns, key_infos => \@keys }; } $db->disconnect; return @rows; } 1; |
続く