テーブル定義のexcelファイル出力(1)

テーブル定義って考えることが多くて大変だし、excel上だけで、作業するのも退屈なので、以前から実際のスキーマ情報からテーブル定義を自動生成したいとは思っていました。DBDesignerというソフトウェアで、実際のDBからテーブル定義をリバースエンジニアリングしてから、カラム名の日本語説明やら備考やらを書き込んで、HTMLリポートで出力ということも試してみたんですが、満足できませんでした。


最近Postgresqlを使うことが多いんですが、postgresqlでは、DBのオブジェクトにCOMMENTを付加することができるので、それを利用して日本語名やら備考を設定していけば、実際のDBからテーブル定義書を出力できるようにできます。

仕様

汚い仕様ですが、例えばカラムに対してのコメントは、「カラム名;備考」というように、;で区切った前がカラム名で;以降が備考というように設定することにしました。pgadmin3というツールを使えばコメントを付加するのは簡単です。できたらMySQLとかも対応できたらいいけど、必要にならないとやらないかも。(MySQLにコメントを付加する機能があるかどうかも知りません。)

実装

リハビリの意味も含めてperlで書きました。

DbDefInfo.pmDBD:PgPP で各テーブル、カラム情報をコメント付きでとってきます。
WriteExcelDbDefInfo.pmSpreadsheet::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;

続く

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


The reCAPTCHA verification period has expired. Please reload the page.

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください