Paul DuBois
paul@kitebird.com
訳者
久保健洋
kubo@jiubao.org
Original Document revision: 1.02
Original Last update: 2003-05-27
翻訳バージョン: 0.9.5
Ruby DBI を使うと、いろんな種類のデータベースを同じAPIでもってrubyから
アクセスすることができます。これは、Perl DBI と perl の関係と同じです。
この記事では Ruby DBI を使用したRubyスクリプトの書き方を説明します。こ
の文書は DBI 仕様書に説明を追加するものであって、DBI 仕様書そのものを
置き換えるものではありません。したがって、DBI 仕様書は必ず読んでくださ
い。本文書で足りない場合は、「参考情報」を参照し
てください。
Ruby DBI のアーキテクチャはおおまかに以下の2つの層(レイヤ)から成り立っ
ています。
Ruby DBI は汎用的なDBI層のコードと、DBDレベルの様々なドライバから成り
立っています。これらのドライバを使用するときにはほとんどの場合、それ用
のソフトウェアを追加でインストールする必要があります。例えば MySQL の
DBDドライバそのものは Ruby で書かれており、DBIから渡されたリクエストを
Ruby MySQL モジュールのリクエストへと変換して渡します。そして、Ruby
MySQL モジュールそのものは C で書かれており、DBDドライバから渡されたリ
クエストを MySQL の C クライアントライブラリへと変換して渡します。
つまり、DBI を使うスクリプトを書いて、MySQL データベースへと接続する場
合には、Ruby MySQL モジュールと C API との両方を追加インストールする必
要があります。Ruby MySQL モジュールについての情報は
「参考情報」に記載されている参考文献を見てください。ここでは MySQL
モジュールはすでにインストールされており、使用できる状態にあるとみなし
ます。
インストール
MySQL の C クライアントライブラリと Ruby MySQL モジュールの準備が終わっ
たら、Ruby DBI モジュールのインストールが可能となります。Ruby DBI モ
ジュールは以下のサイトから取得できます。
http://rubyforge.net/projects/ruby-dbi/DBIモジュールは圧縮されたtarファイルとして配布されているので、ダウン ロードした後に展開する必要があります。例えば、現在のバージョンが 0.0.19 とすると、配布ファイルは以下のコマンドで展開されます。
% tar zxf ruby-dbi-all-0.0.19.tar.gz (訳者註: GNU tar の場合) % gunzip < ruby-dbi-all-0.0.19.tar.gz | tar xf - (訳者註: 商用UNIXのOS附属の tar の場合)配布パッケージを展開したら、展開したファイルのトップディレクトリへ移動し、 そのディレクトリにある setup.rb スクリプトを使ってインストールするパッ ケージの特定を行います。 一般的には、以下のように config 引数の後にはなにも引数をつけません。
% ruby setup.rb configこのように実行すると、デフォルトですべてのドライバがインストール対象と なります。インストールするドライバを選択したい場合、--with オプションに ドライバの名前を列挙します。例えば、DBI のメイン部分と MySQL の DBD レ ベルのドライバのみをインストールするときは、次のコマンドを発行します。
% ruby setup.rb config --with=dbi,dbd_mysqlインストールパッケージの設定が終わったら、ビルドしてインストールします。
% ruby setup.rb setup % ruby setup.rb installこのコマンドは root で実行しなければならない場合があります。
この記事では以降、以下の表記を慣習として使用します。
Ruby DBI モジュールがインストールされているなら、Rubyプログラムから
MySQL サーバへ接続できるようになっています。
この記事では、サーバはローカルホストで動いており、test という名前のデー
タベースへ testuser、testpass というユーザ名、パスワードで接続できると
みなします。testuser を作成するには、MySQL の root ユーザでサーバへ接
続して次の文を発行してください。
mysql> GRANT ALL ON test.* TO 'testuser'@'localhost' IDENTIFIED BY 'testpass';test データベースがない場合は、次の文を発行してデータベースを作成して ください。
mysql> CREATE DATABASE test;別のサーバホスト、ユーザ名、パスワード、データベース名を使用したい場合 は、この記事の以降のスクリプトにでてくる値を適切な値に置き換えてくださ い。
次のスクリプトsimple.rbは、サーバへ接続して、サーバのバージョン を取得して、表示して、接続を切るだけの短いDBIプログラムです。このスク リプトは「参考情報」のリンクからダウンロードでき ます。テキストエディタで直接作成してもかまいません。
# simple.rb - Ruby DBI を使用した簡単なMySQLスクリプト require "dbi" begin # MySQLサーバへ接続 dbh = DBI.connect("dbi:Mysql:test:localhost", "testuser", "testpass") # サーババージョンの文字列を取得して、表示する。 row = dbh.select_one("SELECT VERSION()") puts "Server version: " + row[0] rescue DBI::DatabaseError => e puts "An error occurred" puts "Error code: #{e.err}" puts "Error message: #{e.errstr}" ensure # サーバから切断 dbh.disconnect if dbh endsimple.rbスクリプトからはDBIの基本的なコンセプトが読み取れます。 このセクションの残りではDBIがどのように働くかを説明し、次のセクション からDBIプログラミング特有の側面について例を出しつつ詳細に記述していき ます。
simple.rbはDBIモジュール要求するrequireの行から始ま ります。この行がないと、DBI メソッド呼び出しが失敗します。スクリプトの 残り部分はbegin/rescue/ensureの中に置いてあります。
simple.rbはデータベースハンドルを使ってselect_oneメソッ ドを呼んでいます。このメソッドは、問い合わせをサーバへ送り、結果列の最 初の行を配列として戻すメソッドです。問い合わせSELECT VERSION()は値をひとつ戻します。したがって、バージョン文字列は row[0]として利用できます。これは配列の最初のそしてただひとつ の項目です。スクリプトを走らせると、結果はこのように表示されます。
% ruby simple.rb Server version: 4.0.13-logエラーが起こると例外が上ります。様々な種類の例外が起こり得ます。 しかし、データベースの操作に関するエラーのほとんどは DatabaseError例外を引き起こします。この例外のオブジェクトは errとerrstrの属性を持っていて、エラー番号とエラーの 内容を記述したメッセージを意味します。 simple.rbはデータベース例外に関連するこれらの値を取得して出力 し、その他の種類の例外は無視します。(本来ならば、rescue ブロック内で 例外を作成し、呼び出し元のコードにわたして処理するべきです。)
simple.rbはdisconnectメソッドを呼んでサーバへの接続を
切ります。切断はensureブロックで行い、問い合せを処理している
最中にエラーが起きても接続が確実に切られるようにします。
問合せ処理
RUby DBI を用いてSQL文を実行する方法はたくさんあります。このセクション ででそのうちの何種類かを使用します。ここで記述されなかった方法について はDBI仕様書を参照してください。
本文書で使用するサンプルスクリプトは、大抵の場合、peopleとい う名前のテーブルを使用します。このテーブルは以下の構造をしています。
CREATE TABLE people ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, # ID番号 name CHAR(20) NOT NULL, # 名前 height FLOAT, # 身長(インチ単位) PRIMARY KEY (id) );
行を返さないSQL文は、データベースハンドルのdoメソッドでもって 実行できます。このメソッドは問い合せ文字列1つを引数にとり、問い合わせ の実行で影響の受けた行を戻します。次の例はdoを何回も使用して、 peopleテーブルを作成し小さなテーブルデータを挿入します。
dbh.do("DROP TABLE IF EXISTS people") dbh.do("CREATE TABLE people ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), name CHAR(20) NOT NULL, height FLOAT)") rows = dbh.do("INSERT INTO people (name,height) VALUES('Wanda',62.5),('Robert',75),('Phillip',71.5),('Sarah',68)") printf "%d rows were inserted\n", rowsINSERT文の実行箇所を見てください。スクリプト内でどれだけの数 の行がテーブルに追加されたか取得し、画面に表示しています。
SELECTやSHOWといったSQL文は行を戻します。このような SQL文を処理するには、SQL文をサーバへ送信して実行し、生成された結果セッ トに含まれる行を取得して、最後に結果セットを破棄する必要があります。
実行方法は何種類もありますが、ここではprepareメソッドを呼んで 文ハンドルを生成し、このハンドルを使用して文を実行し、実行結果を得て、 結果セットを解放する方法を使用します。
sth = dbh.prepare(SQL文) sth.execute ... 行を取得 ... sth.finishSQL文を直接executeにわたして、prepareの呼び出しをス キップする方法もあります。
sth = dbh.execute(SQL文) ... 行を取得 ... sth.finishSQL文を実行した後に結果セットから検索結果を得る方法は何種類もあります。 そのうちの一つとして、fetchをブロックなしで呼び出しで、 nilが戻るまでループする方法があります。
sth = dbh.execute("SELECT * FROM people") while row = sth.fetch do printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finishfetchはイテレータとして使う方法もあります。これは eachをイテレータとして呼んだときと同じです。fetchも eachも等価です。
sth = dbh.execute("SELECT * FROM people") sth.fetch do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finish sth = dbh.execute("SELECT * FROM people") sth.each do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finishfetchとeachはDBI::Rowオブジェクトを生成しま す。行の中身をアクセスするには、DBI::Rowオブジェクトのメソッドを使用し ます。
val = row.by_index(2) val = row.by_field("height")
val = row[2] val = row["height"]
sth = dbh.execute("SELECT * FROM people") sth.each do |row| row.each_with_name do |val, name| printf "%s: %s, ", name, val.to_s end print "\n" end sth.finish
sth = dbh.execute("SELECT * FROM people") while row = sth.fetch_hash do printf "ID: %d, Name: %s, Height: %.1f\n", row["id"], row["name"], row["height"] end sth.finish sth = dbh.execute("SELECT * FROM people") sth.fetch_hash do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row["id"], row["name"], row["height"] end sth.finishデータベースハンドルのメソッドを利用すると、execute-fetch-finishの一連の 操作を個々に実行しなくても良くなります。次のメソッドはこれらの仕事を一 度に行い、結果を戻します。
row = dbh.select_one(statement) rows = dbh.select_all(statement)select_oneは問合せを実行して最初の一行を配列として戻します。 問合せが一行も返さないときはnilを戻します。 select_allはDBI::Rowオブジェクトの配列を戻します。 (このオブジェクトの中身は以前説明した方法にアクセスできます。) 問合せが一行も返さないときは空の配列となります。
MySQLドライバは結果セットのメタデータを調べて行の値を対応するRubyのデー
タタイプへ変換します。(言い換えると、例えば、people
テーブルから取り出されたid、name、heightの
値がFixnum、String、Floatのオブジェクトとし
て戻されるということです。) しかし、カラムの値がNULLの場合は
注意してください。この場合は、NULL値は結果セットではnilとして
表現され、その種類はNilClassとなります。また、どのような型に
変換するかはDBI仕様書では規定されておらず、DBDドライバによって変換先が
異なる場合があります。
クオート、プレースホルダー、パラメータ束縛
Ruby DBI にはプレースホルダー機構があり、問合せ文字列のデータの 値を直接取り込まなくても良いようになっています。代わりにSQL文の中に特 別な印をつけて、データの値が入る場所を示します。SQL文を実行するときに は、プレースホルダーにバインドする値を与えておきます。DBIはプレースホ ルダーの現れる場所のSQL文をバインドした値に置き換えます。このとき文字 列の値はクオートで囲って、必要なら特殊文字をエスケープします。これを使 うと、与えようとする文字に特殊文字が含まれているかどうか気にせずに、ま た、自身でクオートで囲うことなしに簡単にSQL文を組み立てることができま す。プレースホルダー機構はNULL値も適切にあつかいます。 nilをデータとして与えるだけで、クオートされていない NULL値としてSQL文中に置かれます。
次の例はこれがどのように働くかを説明します。例として、クオートを含む Na'ilという名前で身長が76インチの人をpeopleテーブルにに追加す ると想定しましょう。まず、データがINSERT文のどこに入るかを示 すために、プレースホルダーの印'?'をシングルクオートなしで使 い、次いで、doの引数にSQL文につづけて、追加の引数としてデータ を与えます。
dbh.do("INSERT INTO people (id, name, height) VALUES(?, ?, ?)", nil, "Na'il", 76)doにより生成されてサーバへ送られる最終的なSQL文は次のようにな ります。
INSERT INTO people (id,name,height) VALUES(NULL,'Na\'il',76)SQLを何回も実行しようと考えている場合は、文ハンドルを得るために prepareで準備してから、executeでデータを引数として与 えながら実行することができます。 people.txtという名のデータファイルに、peopleテーブルに挿 入する名前/身長のペアがタブ区切りで行毎にはいっていると仮定しましょう。 次の例はファイルを呼んで一行のデータを得て、前もって準備した INSERT文を各行ごとに実行します。
# 挿入ループで使用する文ハンドルを用意する。 sth = dbh.prepare("INSERT INTO people (id, name, height) VALUES(?, ?, ?)") # ファイルから一行づつ読んで、値に分けて、データベースへ挿入する。 f = File.open("people.txt", "r") f.each_line do |line| name, height = line.chomp.split("\t") sth.execute(nil, name, height) end f.closeSQL文を prepare した後にループ内で何回も実行したほうが、ループ毎に doを呼び出す(繰り返し毎にprepareとexecuteの 両方を呼ぶのと同等)よりも効率的です。問い合わせ実行計画を準備して、 executeの呼び出し毎に実行計画を再利用するデータベースの場合、 これは重大な効率上の差異となります。MySQLは実行計画を再利用はできませ んが、Oracleは再利用します。
SELECT文にプレースホルダーを使う場合、SQL文を最初の準備するか にするかどうかで適切な方法がかわります。
sth = dbh.prepare("SELECT * FROM people WHERE name = ?") sth.execute("Na'il") sth.fetch do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finish
sth = dbh.execute("SELECT * FROM people WHERE name = ?", "Na'il") sth.fetch do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finish
quoteメソッドはデータ値をクオートで囲い、特殊文字をエスケープ して、結果を戻します。これは他のプログラムで実行するSQL文を組み立てる のに便利です。例えば、データファイルpeople.txtを読んで、 INSERT文のかたまりに変形して、コマンドラインクライアントの mysqlといったプログラムで処理させる場合、次のようにします。
# ファイルから一行づつ読んで、値に分けて、INSERT文を書き出す。 f = File.open("people.txt", "r") f.each_line do |line| name, height = line.chomp.split("\t") printf "INSERT INTO people (id, name, height) VALUES(%s, %s, %s);\n", dbh.quote(nil), dbh.quote(name), dbh.quote(height) end f.close
INSERTやDELETEといった結果セットを戻さないSQL文の場 合、doメソッドは処理された行数を戻します。
SELECTといった行を戻すSQL文の場合、executeを呼んだ後 に文ハンドルを使い、結果セットの行数やカラム数、個々のカラムの情報を得 ることができます。
sth = dbh.execute(query) puts "問い合わせ: " + query if sth.column_names.size == 0 then puts "問い合わせには結果セットがありません。" printf "影響を受けた行の数: %d\n", sth.rows else puts "問い合わせには結果セットがあります。" rows = sth.fetch_all printf "行数: %d\n", rows.size printf "カラム数: %d\n", sth.column_names.size sth.column_info.each_with_index do |info, i| printf "--- カラム %d (%s) ---\n", i, info.name printf "精度: %s\n", info.precision printf "スケール: %s\n", info.scale end end sth.finish注意事項: 古いバージョンのこの文書では、SELECT文の結果の行数 をsth.rowsで得ることができると述べていましたが、これはサポー トされていません。(現在のところ MySQL ドライバではたまたま動きますが、 この挙動は信頼しないでください。)
ハンドルを生成するメソッドのうちいくつかはコードブロック付きで呼ぶこと ができます。ブロック付きで実行した時、ハンドルはコードブロックのパラメー タとして渡され、ブロックの実行が終了したら自動的にハンドルが解放されま す。
# connectはコードブロックをとり、データベースハンドルをブロックに渡します。 # ブロックの最後では自動的にハンドルを切断します。 DBI.connect("dbi:Mysql:test:localhost", "testuser", "testpass") do |dbh| # prepareはコードブロックをとり、文ハンドルをブロックに渡します。 # ブロックの最後では自動的にfinishを呼びます。 dbh.prepare("SHOW DATABASES") do |sth| sth.execute puts "Databases: " + sth.fetch_all.join(", ") end # executeはコードブロックをとり、文ハンドルをブロックに渡します。 # ブロックの最後では自動的にfinishを呼びます。 dbh.execute("SHOW DATABASES") do |sth| puts "Databases: " + sth.fetch_all.join(", ") end end他にコードブロックを取るtransactionメソッドがあります。 使い方は「トランザクションサポート」で説明されます。
以前、例に出したsimple.rbスクリプトはサーバへ接続するのに以下の ようにDBIのconnectメソッドを用いています。
dbh = DBI.connect("dbi:Mysql:test:localhost", "testuser", "testpass")connectの第一引数はデータソース名(DSN, data source name)で、 作成しようとする接続の種類を区別します。残りの2つの引数はMySQLアカウ ントのユーザ名とパスワードです。
DSN は以下の書式ならどれでも受け付けます。
dbi:driver_name dbi:driver_name:db_name:host_name dbi:driver_name:key=val;key=val...DSNは常にdbiもしくはDBIで始まります。(すべて大文字か すべて小文字かのどちらかです。両者の混在は受け付けません。) 続けて、ド ライバ名となります。MySQLの場合はドライバ名はMysqlです。正確 に先頭のみを大文字にして用いたほうが良いでしょう。(DBI仕様書にはドライ バ名は大文字小文字どちらでも良いと指示されてますが、最新の 0.0.18 でも それが常に真とはかぎりません。) 他のドライバを使用する場合は、適切なド ライバ名を使う必要があります。
dbi(もしくはDBI)とドライバ名は常にDSNに与える必要が あります。ドライバ名に続けて何も指定されてないときは、ドライバは(おそ らく)デフォルトのデータベースとホスト名を使って接続しようとします。 MySQLドライバの場合はデータベース名を指定する必要があるので、DBIを使っ たMySQLプログラミングでは第一の書式は使えません。他の書式を使ってくだ さい。第二の書式は2つの値を要求します。コロンで区切られたデータベース 名とホスト名です。第三の書式はセミコロンで区切られた param=valueの形式でパラメータの指定を行います。次のDSNはすべ て同じ意味をもちます。
dbi:Mysql:test:localhost dbi:Mysql:host=localhost;database=test dbi:Mysql:database=test;host=localhostparam=valueの形式を使ったDSNの構文は最も柔軟です。というのも、 パラメータを任意の順番で指定できるからです。また、ドライバ固有のパラメー タを指定できる可能性もあります。ドライバは受け付けることのできる接続パ ラメータを拡張することができるからです。MySQLの場合は、hostと databaseの他の追加のパラメータはport、 socket、flagです。(これらのパラメータは、 DBD::Mysqlの下にある Ruby MySQL モジュールの real_connectメソッドの対応するパラメータに渡されます。)
DBIメソッドが失敗するとDBIは例外を上げます。DBIメソッドはどんな例外で も上げる可能性はありますが、データベースに関する操作に対しては、それに 関係する例外クラス、DatabaseErrorがあります。このクラスの例外 オブジェクトにはerr、errstr、stateという3 つの属性があります。DBIドキュメントはこれらの属性の意味を特定してはい ませんが、それぞれエラー番号、説明的なエラー文字列、そしてある種の「標 準」エラーコードをあらわしているように見えます。MySQLドライバは現時点 ではエラー文字列のみを提供しています。しかし、エラー番号を同様に戻すよ うにするドライバへパッチは簡単なことです。両方の値が利用可能と仮定する と、例外が起こったときにこれらの値を得るには次のようにします。
rescue DBI::DatabaseError => e puts "An error occurred" puts "Error code: #{e.err}" puts "Error message: #{e.errstr}"実行しようとするスクリプトが何をしているのかデバッグ情報を実行の順序ど おりに得るには、トレース可能な状態にしてください。そうするには、まず最 初にdbi/traceモジュールを読み込みます。
require "dbi/trace"dbi/traceはdbiモジュールによって自動的には読み込まれ ません。というのも、AspectRモジュールのバージョン0.3.3以降に依存するか らです。AspectRはあなたのマシンに存在しないかもしれません。
dbi/traceモジュールはtraceメソッドを提供します。 traceメソッドでトレースのモードと出力先を設定をします。
trace(mode, destination)modeは 0(off)、1、 2、3 の値をとります。 destinationはIOオブジェクトでないといけません。 デフォルト値はmodeは2で、destinationは STDERRです。
traceはクラスメソッドとして呼び出されると、続いて作成されるす
べてのハンドルに対して影響します。または、ドライバ、データベースハンド
ル、文ハンドルに対して個々にインスタンスメソッドとして呼び出すこともで
きます。インスタンスメソッドとして呼ばれると、そのオブジェクトに由来す
るすべてのオブジェクトに対してもトレースの設定が引き継がれます。例えば、
データベースハンドルに対してトレースを有効にすると、そこから作成された
文ハンドルのトレースの設定は同じになります。
トランザクション・サポート
トランザクションの使用方法はデータベースによって異なりますが、DBIはト ランザクションを抽象化して、同じ手順で使える機能を提供します。しかし、 抽象化が使えるかどうかは使用しているデータベースエンジンがトランザクショ ンをサポートしているかどうか、さらには使用するドライバがDBIレベルの抽 象化を実装しているかどうかに依存します。MySQLドライバの場合は、この抽 象化は DBI 0.0.19 より前では機能していません。したがって、トランザクショ ンを実行するには明示的にSQL文を使って自動コミットレベル、コミット、ロー ルバックを制御しないといけません。例えば以下のようにです。
dbh.do("SET AUTOCOMMIT=0") dbh.do("BEGIN") ... statements that make up the transaction ... dbh.do("COMMIT")DBI 0.0.19 以降では、MySQL でトランザクションの抽象化を利用できます。 抽象化の側面のひとつに、自動コミットのレベルをデータベースハンドルの AutoCommit属性の指定で設定できることにあります。
dbh['AutoCommit'] = true dbh['AutoCommit'] = false自動コミットが無効な間は(falseを設定)、トランザクションは2つ の方法で制御できます。次の例でその2つのアプローチを説明します。例とし てaccountテーブルを用い、資金をひとりの人から別の人にわたしま す。
dbh['AutoCommit'] = false begin dbh.do("UPDATE account SET balance = balance - 50 WHERE name = 'bill'") dbh.do("UPDATE account SET balance = balance + 50 WHERE name = 'bob'") dbh.commit rescue puts "transaction failed" dbh.rollback end
dbh['AutoCommit'] = false dbh.transaction do |dbh| dbh.do("UPDATE account SET balance = balance - 50 WHERE name = 'bill'") dbh.do("UPDATE account SET balance = balance + 50 WHERE name = 'bob'") end
DBIはデータベースに依存する特別な機能を利用するためのfunc メソッドを提供してます。例えば MySQL の C API には、使用中のコネクショ ンでの最新のAUTO_INCREMENTの値を戻すmysql_insert_id()関数があ ります。Ruby MySQL モジュールはデータベースハンドルのメソッドinsert_id を経由してこの関数を呼び出します。また、DBD::Mysql は DBI の funcメカニズムを経由してinsert_idにアクセスします。
funcの第一引数にはデータベース固有のメソッドの名前を指定しま す。残りの引数には必要ならばデータベース固有のメソッドが必要とする値を わたします。insert_idメソッドは追加の引数を必要とはしません。 したがってAUTO_INCREMENTの最新の値を得るには次のようにします。
dbh.do("INSERT INTO people (name,height) VALUES('Mike',70.5)") id = dbh.func(:insert_id) puts "ID for new record is: " + id.to_sDBD::Mysqlによりサポートされるその他のドライバ固有のメソッド は以下のとおりです。
dbh.func(:createdb, db_name) 新しいデータベースを作成する。 dbh.func(:dropdb, db_name) データベースを削除する。 dbh.func(:reload) リロード操作を実行する。 dbh.func(:shutdown) サーバをシャットダウンする。あなたの使用しているMySQLクライアントライブラリのバージョンが MySQL 4 以上の場合、createdbとdropdbが使用できないことに注意 してください。これらのメソッドは MySQL 4 より Ruby MySQL モジュールで 使用してはいけない関数(deprecated functions)となっています。
同じことを実現するのにドライバ固有の機能を使用した場合と通常のSQLを発
行して実現する方法の2つがある場合、ドライバ固有の機能を使用したほうが
効率的である場合があります。例えば、
SELECT LAST_INSERT_ID() を発行した結果は
DBD::Mysqlのinsert_id関数の戻り値でも得ることができ
ます。両者ほほとんどの場合同じ結果を戻します。しかしinsert_id
関数のほうがより効率的です。というのもinsert_idは、問い合わせ
を発行することなしにクライアント側に保持された値を戻すからです。
効率上の利点を追及するには、注意深く関数呼び出しを行う必要があります。
insert_idの値はそれぞれの問い合せの実行毎にリセット
されます。そのため、AUTO_INCREMENT値を生成する問い合わせを発
行した後、他の問い合わせを発行する前にアクセスしないといけません。対照
的にLAST_INSERT_ID()の値はサーバ側にもっと長い時間保持されま
す。AUTO_INCREMENT値を生成する問い合わせが発行されない限りリ
セットされることはありません。
その他の便利な機能
DBI::Utilsモジュールには興味深いメソッドがいくつかあります。
elapsed = DBI::Utils::measure do dbh.do(query) end puts "Query: " + query puts "Elapsed time: " + elapsed.to_s
sth = dbh.execute("SELECT * FROM people") rows = sth.fetch_all col_names = sth.column_names sth.finish DBI::Utils::TableFormatter.ascii(col_names, rows)結果の出力は以下のようになります。
+----+---------+--------+ | id | name | height | +----+---------+--------+ | 1 | Wanda | 62.5 | | 2 | Robert | 75.0 | | 3 | Phillip | 71.5 | | 4 | Sarah | 68.0 | +----+---------+--------+
DBI::Utils::XMLFormatter.table(dbh.select_all("SELECT * FROM people"))結果の出力は以下のようになります。
<?xml version="1.0" encoding="UTF-8" ?> <rows> <row> <id>1</id> <name>Wanda</name> <height>62.5</height> </row> <row> <id>2</id> <name>Robert</name> <height>75.0</height> </row> <row> <id>3</id> <name>Phillip</name> <height>71.5</height> </row> <row> <id>4</id> <name>Sarah</name> <height>68.0</height> </row> </rows>
この記事の例で使用したスクリプトは以下の場所よりダウンロードできます。
http://www.kitebird.com/articles/ここには "Using the Ruby MySQL Module(英語)" という記事があり、 DBD::Mysql(DBD層のMySQLドライバ)の土台となっているRuby MySQLモジュール の説明をしてます。
Ruby DBI を使用するのに役立つ情報は以下の場所より見付けられます。
http://ruby-dbi.sourceforge.net/
http://aspectr.sourceforge.net/
http://www.ruby-lang.org/
http://www.mysql.com/