今日もプログラミング

IT技術とかプログラミングのこととか特にJavaを中心に書いていきます

JDBCのカタログとかスキーマについて調べる(Oracle、SQL Server、MySQL)

JDBCには便利な機能があって、テーブルや列のメタ情報を取得できる(java.sql.DatabaseMetaDataのgetTablesとかgetColumnsとか)。

で、これらのメソッドの引数にはカタログとかスキーマを渡す必要がある。

これって何なの?というのを調べてみたい。

 

ちなみに、スキーマはどのDBMSでもだいたいスキーマだけど、カタログの方はDBMSによって違う。何なのかはDatabaseMetaData#getCatalogTermsにより調べられる。

 

手元にはOracle(12c)、SQL Server(2012)、MySQL(5.6)があるので、こいつらについて調べてみる。

 

Oracleのカタログとスキーマ

catelog term (なし)
現在のカタログの取得 (なし)
現在のカタログの変更 (なし)
現在のスキーマの取得 Connection#getSchema ※Oracle JDBC Driver 12c以降
SQL: SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL
現在のスキーマの変更 Connection#setSchema(スキーマ名) ※Oracle JDBC Driver 12c以降
SQL: ALAER SESSION SET CURRENT_SCHEMA=スキーマ

Oracleではカタログにあたるものは無いようだ。

java.sql.ConnectionにはgetSchemaとかsetSchemaメソッドがあって、これらでスキーマを取得したり変更できたりするが、これはJava 1.7で追加されたもの。

古いJDBCドライバは対応していないようで、SQLを投げる必要がある。

 

SQLServerのカタログとスキーマ

catelog term database
現在のカタログの取得 Connection.getCatalog
SQL: SELECT DB_NAME()
現在のカタログの変更 Connection.setCatalog(カタログ名)
SQL: USE カタログ名
現在のスキーマの取得 SQL: SELECT default_schema_name FROM sys.database_principals WHERE name = ユーザ名
現在のスキーマの変更 (なし)

SQL Serverにおけるカタログはデータベースのことだ。

スキーマについては、ユーザごとに既定のスキーマが定義されている。

既定のスキーマはデータベースごとに設定するので、上のSQLでは一意にならないんじゃない?と思ったが、現在使用しているデータベースのが返されるようだ。

これは、USE ~のように接続中に手軽に変えるものではなく、ALTER USER文を使う必要がある。

なお、Connection.getSchemaやsetSchemaを呼んだらjava.sql.SQLFeatureNotSupportedExceptionという例外が出てしまった。

 

MySQLのカタログとスキーマ

catelog term database
現在のカタログの取得 Connection.getCatalog
SQL: select database()
現在のカタログの変更 Connection.setCatalog(カタログ名)
SQL: use カタログ名
現在のスキーマの取得 (なし)
現在のスキーマの変更 (なし)

MySQLにおけるカタログも、データベースのことだ。

スキーマにあたるものは無い。

なお、Connection.setCatalogを呼ばずに直接USE文を投げると、Connection.getCatalogに反映されないようだ。

 

という訳で、カタログとかスキーマについて調べてみたので、今度は実際にDatabaseMetaData.getTablesで何が取れるのかを調べてみたい。

 

OracleでgetTables

Oracleではあるデータベースへの接続から別のデータベースは見られないはず。

データベースは1つだけ、スキーマはSCH1とSCH2の2つを用意し、それぞれにテーブルTESTを用意した。

ログインユーザはSCH1。

現在のスキーマgetTablesの引数のスキーマ取得できたテーブル
SCH1 null SCH1.TEST
SCH2.TEST
SCH1 SCH1.TEST
SCH2 SCH2.TEST
SCH2 null SCH1.TEST
SCH2.TEST
SCH1 SCH1.TEST
SCH2 SCH2.TEST

nullを指定した場合は全スキーマのテーブルが返ってくるということだ。

 

SQL ServerでgetTables

データベースDB1、DB2を用意し、それぞれにスキーマSCH1、SHC2を用意し、すべてにテーブルTESTを用意した。

データベースとスキーマの両方があるのでめんどいなあ。

現在のカタログ現在のスキーマgetTablesの引数のカタログgetTablesの引数のスキーマ取得できたテーブル
DB1 SCH1 null null DB1.SCH1.TEST
DB1.SCH2.TEST
DB1 null DB1.SCH1.TEST
DB1.SCH2.TEST
DB2 null DB2.SCH1.TEST
DB2.SCH2.TEST
DB1 SCH1 DB1.SCH1.TEST
DB1 SCH2 DB1.SCH2.TEST

全パターン書くのはめんどいので、代表的なパターンのみを書いた。

カタログ名を省略すると、Connection.getCatalogの値が補完されるようだ。

スキーマ名を省略すると、全スキーマが対象となる。

Javadocを見ると、両方「null means that the catalog/schema name should not be used to narrow the search」なんだけどなあ。

 

MySQLでgetTables

データベースDB1、DB2を用意し、それぞれにテーブルTESTを用意した。

現在のカタログgetTablesの引数のカタログ取得できたテーブル
null null (なし)
DB1 DB1.TEST
DB2 DB2.TEST
DB1 null DB1.TEST
DB1 DB1.TEST
DB2 DB2.TEST
DB2 null DB2.TEST
DB1 DB1.TEST
DB2 DB2.TEST

カタログ名を省略すると、Connection.getCatalogの値が補完されるようだ。

あと、MySQLではテーブル名に「xxx.<テーブル名>」と指定しても、「xxx.」が無視されて検索されるようだ。

 

まとめ

  • カタログとかスキーマDBMSによって違うので、ちゃんと調べて使わないと。
  • DatabaseMetaData#getTablesでは、カタログを省略すると現在のカタログ、スキーマを省略すると全スキーマが対象になるようだ(とりあえずOracleSQL ServerMySQLの話。他のDBMSは違うかも)

 

どのDBMSでもちゃんと動くツールを作るのは大変だろうなあ…。

 

OS Windos 7
Oracle 12c
JDBC Driver 12.1.0.2
JDBC Driver 11.2.0.4
SQL Server 2012
JDBC Driver 4.1
MySQL 5.6
JDBC Driver 5.1.34