MySQLもうひとつのTclインターフェース「mysqltcl」

 mysqltclは、 Fbsqlと同じく、RDBMS MySQLに対し、 TclスクリプトからSQL文を発行してアクセスするための拡張ライブラリです。 MySQLの前身msqlへのTclインターフェースとして誕生(Hakan Soderlundさん作)し、 MySQL用に移植(Gregory Gulikさん作)されたものの、しばらくはTcl 7.6専用の時代が続きました。 2002年に Tobias RitzauさんによってTcl8に対応したバージョン2.0としてリニューアルされ、 最新の環境でも使えるようになりました。 実験室ではVine Linux 3.1(カーネル2.4.27)、 Tcl/Tk 8.5a1、MySQL 5.0.15 においてmysqltcl 3.01の動作を確認したので、 この環境下での使用法を紹介します。

インストールしよう(Windows編)
 MS-Windows用には、 Windows Version of mysqltcl のページでコンパイル済みのバイナリDLLが配布されています。 これをダウンロードし、展開してできたディレクトリ「mysqltcl-win-2.0」 をTclのライブラリディレクトリ(C:\Program Files\Tcl\libなど) に移動させればインストールは完了です。

インストールしよう(Linux編)
 ソースコードは上記のホームページから配布されています。 コンパイルに入る前に、 libmysqlclient.so.*のあるディレクトリにLD_LIBRARY_PATHを通すか、 ld.so.confに書いてldconfigしておきます。 これはインストール時にpkgIndex.tclを正しく作るために必須の行動です。 実験室ではMySQLのdocumentationの このページ にならっているので、/usr/local/mysql/lib/mysql にあります。 /usr/local/lib/mysql にある環境の方が多いかもしれません。

% export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/mysql/lib/mysql
% ./configure --with-tcl=/usr/local/lib \
  --with-mysql-include=/usr/local/mysql/include/mysql \
  --with-mysql-lib=/usr/local/mysql/lib/mysql
%make
%su root
#make install

configureでは、--with-tclでtclConfig.shのあるディレクトリを、 --with-mysql-includeでMySQLのヘッダファイルがあるディレクトリを、 --with-mysql-libでMySQLのライブラリがあるディレクトリを指定します。 インストールできたらtclshを起動し、「package require mysqltcl」と打ちます。 バージョン番号が出ればOKです。

たらふく詰め込みすぎのサンプル

package require mysqltcl

set con [mysqlconnect -host localhost -port 3306 \
  -user urano -password uranpass -db udb -encoding euc-jp]

set sql {
  SELECT EMPNO, USERNAME, KANJI_NAME
    FROM USERS WHERE USERNAME LIKE '%U%'
}
puts "*** 結果集合を行のリスト(リストのリスト)で返す例 ***"
set resultset [mysqlsel $con $sql -list]
foreach row $resultset {
  puts $row
}

puts "*** 結果集合を1行ずつフェッチする例 ***"
set rowcount [mysqlsel $con $sql]
puts "${rowcount}行のレコードが 見つかりました。"
set row [mysqlnext $con]
while {$row > 0} {
  puts $row
  set row [mysqlnext $con]
}

puts "*** 結果集合の各行に定義した処理をかける例 ***"
set rowcount [mysqlsel $con $sql]
puts "${rowcount}行のレコードが 見つかりました。"
mysqlmap $con {empno username kanji_name} {
  puts "<$empno>\t<$username>\t<$kanji_name>"
}

set sql {
  UPDATE USERS
     SET JOB_NAME = '閑職'
   WHERE USERNAME = 'uranom'
}
set rowcount [mysqlexec $con $sql]
puts "${rowcount}行のデータを更新しました。"
set sql {
  SELECT * FROM USERS
   WHERE USERNAME = 'uranom'
}
set resultset [mysqlsel $con $sql -list]
puts $resultset

puts [mysqlinfo $con info]
puts [mysqlinfo $con databases]
puts [mysqlinfo $con dbname]
puts [mysqlinfo $con host]
puts [mysqlinfo $con tables]
mysqlclose $con
# end.

上のスクリプトがMySQLデータベースに接続し、SQL文を発行する簡単なサンプルです。 接続はmysqlconnectコマンド、 切断はmysqlcloseコマンドで行います。 SELECT文の発行はmysqlselコマンド、 DML文(INSERT文、UPDATE文、DELETE文)の発行はmysqlexecコマンドで行います。 まず日本語の扱いですが、 mysqltclは、mysqlconnectコマンドのオプションとして-encodingがあり、 これにエンコーディングを設定することで、MySQL serverとの通信時にコード変換をしてくれます。 この-encodingは、MySQLではなくTclが認識するエンコーディングを指定する点に注意して下さい。 例えば「ms932」や「euc-jp」になります。
それで、SQL文自体には特に変わったことはないのですが、 mysqlselコマンドはサンプルのように3種類の使い方ができ、 便利なものを使い分けることが可能です。 最初の例はSELECT文の結果集合を各行のリスト、 つまりリストのリストとして取得できるもので、 「y行目のレコードの第xカラム」のように指定してデータを取り出したい二次集計系のツールでよく使います。

2番目の例は典型的なカーソルフェッチループで、 mysqlnextコマンドで1行ずつ結果集合を取り出して処理するので、 テキスト出力などに便利ですが、おそらく一番遅いと思います (内部機構を見たわけではありませんが)。 mysqlnextコマンドは結果集合の最後の行まで達すると次からは要素零のリストを返してくるので、 これを見てループを抜けるように書くとよいでしょう。

3番目の例が面白くて効率のよいおすすめの使い方です。 各行に対する処理(スクリプト)を、最後の引数で指定しておき、 各カラムの値に対応するバインド変数を使って処理をさせることができるので可読性も充分。 慣れてくると一番直感的な方法といえましょう。

さて、最後に重大な注意がひとつ! mysqlexecコマンドは影響を与えた(更新した)行数を返しますが、 UPDATE文のWHERE句に該当して対象行となった場合でも、 元の行データが更新後のデータと全く同じ場合、 「更新した」(effected)行にはカウントされないようです。 これはOracleなどとは全く違う特徴なので、充分注意されなければいけませんね。

拡張レビュー分室 top
(first uploaded 2002/10/14 last updated 2006/04/04, MISUMI URANO)