質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JDBC

JDBC(Java DataBase Connectivity)は、Javaとリーレーショナルデータベースに接続させる基本的なAPIです。Java上でSQLステートメントを発行することで、データベースの種類に影響を受ないDB操作を可能とします。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

21522閲覧

ResultSetからテーブル名や別名を取得する方法

artery

総合スコア43

JDBC

JDBC(Java DataBase Connectivity)は、Javaとリーレーショナルデータベースに接続させる基本的なAPIです。Java上でSQLステートメントを発行することで、データベースの種類に影響を受ないDB操作を可能とします。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

2クリップ

投稿2016/04/06 00:38

◇ 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));
}
}
}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

手元の PostgreSQL で確認してみましたが、確かに getMetaData() でカラムに対するテーブル名は返ってきていませんね。JDBC のインターフェイスで返ってきていない。ということは Java でその先を探るのは大変だと思います。

質問に対して期待する回答では無く、逆に質問となってしまうのですが、折角 SQL を自分で組み立てて叩いているのに、あえて省略し、曖昧な結果を捉えて、その次にどうしたいのでしょうか?その意図が知りたいです。

質問者様は実務でされていらっしゃるようですので、既に代替策も持っているだろうし、以下、言われなくても、という話だと思いますが、他の開発者様向けに書いておきます。

開発現場によっては、SELECT * での全項目列挙自体禁止しているところもあるでしょう。開発上曖昧になる省略を行う事はバグを誘発する要素にもなりうるため、あくまでも SQL にこだわるのであれば、別名定義(alias)を使えば確実に捉えられる内容ですから、厳密に書くだけではないでしょうか?

まともな開発であれば、取り出した結果に対して DTO を用意すると思うのですが、SQL側の問題であるカラム名の重複を、別のレイヤーで対処するのはとても違和感があります。カラム名が重複するけれど、別な内容になる項目であれば、SQL側でカラム名の別名定義を使って項目の整理をしておき、その別名定義も DTO で捉えるのが自然なやり方ではないでしょうか?

また、あくまでも省略を前提で考えているのであれば、カラム名の重複が発生しても大丈夫な(一番最後を採用する等の)読み取り時のルールを作ってしまうなり、悩みを無視できる状況そのものを仕掛けとして用意してしまえば解決できます。例えば、開発規模等に応じた SQL を極力考えなくても済むタイプの ORM を採用するなどが方法として挙げられるでしょう。

投稿2016/04/06 10:32

ps13zier

総合スコア433

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

A-pZ

2016/04/07 01:55

取得する方法が異なります。
ps13zier

2016/04/07 02:21

『単純にDBの情報を取りたい』という質問に対する答えとしては、私の答えは間違いだと思います。『カラム名が重複する結果を問う SQL を使って得た ResultSet からテーブル名などの情報を得たい』という質問だと捉えて回答したのですが?私の認識が間違っておりましたら申し訳ないです。
artery

2016/04/09 03:30

ご回答、ありがとうございます。 忙しかったので本日になりました。 >『カラム名が重複する結果を問う SQL を使って得た ResultSet からテーブル名などの情報を得たい』 質問の趣旨は、この通りです。 ご指摘の通り、実務のプログラムで select * from ...とするようなことはありません。 背景は、11月くらいから、休みの日に、DBViewerのようなデータベースにアクセスするプログラムを作成しています。 それでselectした結果の表示で、 (* があろうがなかろうが、)カラム名だけではなく、カラムの説明も表示しようとしています。表示スペースは必要ですが、分かり易くなります。 カラムの説明は、ResultSetMetaDataではなく、DatabaseMetaDataから取得することになります。 それでカラムが属するテーブル名を取得したいということです。 同じ理由で、別名が指定された場合も、元のカラム名とテーブル名を取得したいということです。 ちなみにORACLEでは、DatabaseMetaDataからはテーブルの説明やカラムの説明は取得できず、独自のSQLを実行する必要があります。この部分は実装済み。 ご指摘のように、さらにJDBCベースで、カラムに対応するテーブル名を取得するのは無理かと思います。 やるすれば、実行したSQLを文法解析して、DatabaseMetaDataから取得している情報と突き合わせることになります。 これであれば、可能かと思いますが、ここまではやりたくないというのが本音です。 ありがとうございました。
ps13zier

2016/04/09 08: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 から一意なカラムを求めれば解決できますよね。 もっと良いやり方もあるかもしれませんが、一応求めている機能は要求を満たすライブラリを併用すれば(細かい所は大変だと思いますが)実現のハードルは少し下げられると思います。
artery

2016/04/10 01:37

アドバイスをありがとうございます。 MySQLではちゃんと表示されていますが、ORACLEなどで実行して、表示されなかったので、今回の質問となりました。 ORACLEの場合はできないと諦めるか、アドバイスのように実装して頑張るかは、今後決定したいと思います。
guest

0

テーブル情報などの定義情報(メタ情報と呼ばれる)はDatabaseMetaDataが扱っており、ResultSetMetaDataからではJDBCドライバによっては取得できないようです。

なお、メタ情報を取得するサンプルは以下になります。

java

1DatabaseMetaData meta = conn.getMetaData(); 2// 第1引数:カタログ名(nullの場合あり) 3// 第2引数:スキーマパターン名(nullの場合あり) 4// 第3引数:テーブル名パターン(テーブル名を直接指定してもよい。全テーブルは空文字) 5// 第4引数:カラム名パターン(カラム名を直接指定してもよい。すべてのカラムは空文字) 6ResultSet rset = meta.getColumns(null, null, "test", ""); 7while ( rset.next() ) { 8 // 3番目はテーブル名、4番目はカラム名 9 System.out.println(rset.getString(3) + "\t" + rset.getString(4)); 10}

出力結果の例

test id test name

投稿2016/04/07 01:54

A-pZ

総合スコア12011

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問