ResultSetからテーブル名や別名を取得する方法
解決済
回答 2
投稿
- 評価
- クリップ 2
- VIEW 13K+
◇ ResultSetからテーブル名や別名を取得する方法
◇ JDBCのResultSetから取得したカラムの情報を取得できます。
◇ MySQL,PostgreSQL,ORACLEでテストしたところ、以下の結果になりました。
・テーブル名、MySQLは取得できるが、他は不可。
・なので、MySQL以外では、同一名称のカラムがあった場合は、区別がつかない
・MySQLは、別名を指定した場合、カラム名に元のカラム名、カラムラベルに別名が入るが、他は両方に別名が戻ってくる
・なので、MySQL以外は別名を指定されても分からない
・ORACLEやPostgreSQLで、ResultSetからテーブル名や別名を取得する方法があれば、ご教授願います。
・下記サンプルはMySQLのものです。
package jp.avaj.z.sample.lec.s0210;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import jp.avaj.lib.db.DB;
import jp.avaj.lib.debug.L;
import jp.avaj.z.common.db.MyDB;
/**
* Java JDBC ResultSetからメタデータ(カラム情報)を取得する
*/
class GetResultSetMetaDataMySQL {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = MyDB.connectToMySQL();
//
L.p("==== test no 0 - 同一のカラム名がある場合");
String sql = "select * from BOOK inner join PUBLISHER on BOOK.PUBLISHER_ID = PUBLISHER.ID";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
ResultSetMetaData meta = rs.getMetaData();
printMeta(meta);
// クローズ処理はサンプルなのでいい加減(笑)
DB.close(rs);
DB.close(st);
//
L.p("==== test no 1 - 別名が指定された場合");
sql = "select ISBN,NAME BOOKNAME from BOOK";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
meta = rs.getMetaData();
printMeta(meta);
DB.close(rs);
DB.close(st);
//
L.p("==== test no 2 - カラムに対応しない項目がある場合");
sql = "select PRICE*PUBLISHER_ID as VALUE from BOOK";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
meta = rs.getMetaData();
printMeta(meta);
DB.close(rs);
DB.close(st);
}
catch(Exception ex) {
// 例外は無視 ⇒ 実務ではやってはいけない...
ex.printStackTrace();
}
finally {
DB.close(conn);
}
}
private static void printMeta(ResultSetMetaData meta) throws SQLException {
int cnt = meta.getColumnCount();
for (int i=1; i<=cnt; i++) { // 1 オリジンなので注意
L.p("カタログ名="+meta.getCatalogName(i));
L.p("クラス名="+meta.getColumnClassName(i));
L.p("表示幅="+meta.getColumnDisplaySize(i));
L.p("カラムラベル="+meta.getColumnLabel(i));
L.p("カラム名="+meta.getColumnName(i));
L.p("カラムタイプ(SQLタイプ)="+meta.getColumnType(i));
L.p("カラムタイプ(DB固有)="+meta.getColumnTypeName(i));
L.p("カラムサイズ="+meta.getPrecision(i));
L.p("小数点以下の桁数="+meta.getScale(i));
L.p("スキーマ名="+meta.getSchemaName(i));
L.p("テーブル名="+meta.getTableName(i));
L.p("AutoIncrement?="+meta.isAutoIncrement(i));
L.p("CaseSensitive?="+meta.isCaseSensitive(i));
L.p("キャッシュ値?="+meta.isCurrency(i));
L.p("書き込みが必ず成功するか?="+meta.isDefinitelyWritable(i));
L.p("isNullable?="+meta.isNullable(i));
L.p("isReadOnly?="+meta.isReadOnly(i));
L.p("whereで指定可能?="+meta.isSearchable(i));
L.p("isSigned?="+meta.isSigned(i));
L.p("書き込みを成功するか?="+meta.isWritable(i));
}
}
}
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
手元の PostgreSQL で確認してみましたが、確かに getMetaData() でカラムに対するテーブル名は返ってきていませんね。JDBC のインターフェイスで返ってきていない。ということは Java でその先を探るのは大変だと思います。
質問に対して期待する回答では無く、逆に質問となってしまうのですが、折角 SQL を自分で組み立てて叩いているのに、あえて省略し、曖昧な結果を捉えて、その次にどうしたいのでしょうか?その意図が知りたいです。
質問者様は実務でされていらっしゃるようですので、既に代替策も持っているだろうし、以下、言われなくても、という話だと思いますが、他の開発者様向けに書いておきます。
開発現場によっては、SELECT * での全項目列挙自体禁止しているところもあるでしょう。開発上曖昧になる省略を行う事はバグを誘発する要素にもなりうるため、あくまでも SQL にこだわるのであれば、別名定義(alias)を使えば確実に捉えられる内容ですから、厳密に書くだけではないでしょうか?
まともな開発であれば、取り出した結果に対して DTO を用意すると思うのですが、SQL側の問題であるカラム名の重複を、別のレイヤーで対処するのはとても違和感があります。カラム名が重複するけれど、別な内容になる項目であれば、SQL側でカラム名の別名定義を使って項目の整理をしておき、その別名定義も DTO で捉えるのが自然なやり方ではないでしょうか?
また、あくまでも省略を前提で考えているのであれば、カラム名の重複が発生しても大丈夫な(一番最後を採用する等の)読み取り時のルールを作ってしまうなり、悩みを無視できる状況そのものを仕掛けとして用意してしまえば解決できます。例えば、開発規模等に応じた SQL を極力考えなくても済むタイプの ORM を採用するなどが方法として挙げられるでしょう。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
テーブル情報などの定義情報(メタ情報と呼ばれる)はDatabaseMetaDataが扱っており、ResultSetMetaDataからではJDBCドライバによっては取得できないようです。
なお、メタ情報を取得するサンプルは以下になります。
DatabaseMetaData meta = conn.getMetaData();
// 第1引数:カタログ名(nullの場合あり)
// 第2引数:スキーマパターン名(nullの場合あり)
// 第3引数:テーブル名パターン(テーブル名を直接指定してもよい。全テーブルは空文字)
// 第4引数:カラム名パターン(カラム名を直接指定してもよい。すべてのカラムは空文字)
ResultSet rset = meta.getColumns(null, null, "test", "");
while ( rset.next() ) {
// 3番目はテーブル名、4番目はカラム名
System.out.println(rset.getString(3) + "\t" + rset.getString(4));
}
出力結果の例
test id
test name
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.35%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/04/07 10:55
2016/04/07 11:21
2016/04/09 12:30
忙しかったので本日になりました。
>『カラム名が重複する結果を問う SQL を使って得た ResultSet からテーブル名などの情報を得たい』
質問の趣旨は、この通りです。
ご指摘の通り、実務のプログラムで select * from ...とするようなことはありません。
背景は、11月くらいから、休みの日に、DBViewerのようなデータベースにアクセスするプログラムを作成しています。
それでselectした結果の表示で、 (* があろうがなかろうが、)カラム名だけではなく、カラムの説明も表示しようとしています。表示スペースは必要ですが、分かり易くなります。
カラムの説明は、ResultSetMetaDataではなく、DatabaseMetaDataから取得することになります。
それでカラムが属するテーブル名を取得したいということです。
同じ理由で、別名が指定された場合も、元のカラム名とテーブル名を取得したいということです。
ちなみにORACLEでは、DatabaseMetaDataからはテーブルの説明やカラムの説明は取得できず、独自のSQLを実行する必要があります。この部分は実装済み。
ご指摘のように、さらにJDBCベースで、カラムに対応するテーブル名を取得するのは無理かと思います。
やるすれば、実行したSQLを文法解析して、DatabaseMetaDataから取得している情報と突き合わせることになります。
これであれば、可能かと思いますが、ここまではやりたくないというのが本音です。
ありがとうございました。
2016/04/09 17:14
構文解析器を自分で一から作って文法解釈を行うのは大変な手間だと思います。
別な策として "sql parser" などのキーワードでオープンなライブラリを探して使ってみるのはどうでしょうか?
探してみた所 FoundationDB SQL Parser というライブラリが見つかりました。FoundationDB 自体はアップルに買収されて GitHub のものは非公開になっているようですが、Jar 自体は取れるようです。
http://mvnrepository.com/artifact/com.foundationdb/fdb-sql-parser
実際に少し叩いてみましたが、カラムに対してテーブルの修飾名が付いている場合、解釈結果に含まれる個々のカラムを示す ResultColumn クラスにおそらく欲しい情報は入っています。具体的には以下のメソッド
ResultColumn.getTable()
ResultColumn.getExpression().getColumnName() 別名定義時の元カラム名
あと、複数のテーブルを使っていてテーブル名の修飾を省略したカラムがあった場合は、解釈結果で使われているテーブル一覧は当然取れるので、あとは DatabaseMetaData から一意なカラムを求めれば解決できますよね。
もっと良いやり方もあるかもしれませんが、一応求めている機能は要求を満たすライブラリを併用すれば(細かい所は大変だと思いますが)実現のハードルは少し下げられると思います。
2016/04/10 10:37
MySQLではちゃんと表示されていますが、ORACLEなどで実行して、表示されなかったので、今回の質問となりました。
ORACLEの場合はできないと諦めるか、アドバイスのように実装して頑張るかは、今後決定したいと思います。