もっとおらおらOratcl

では、実際にOratclを使ってOracleのデータを操作する方法をご紹介します。

●結果を1行だけ返すSELECT文
さて、まずは簡単な問合せを実行して、結果を表示してみます。

package require Oratcl
set con [oralogon "DEVUSER/DEVPASS@DEVDB"]

set s "SELECT COUNT(*) FROM FAC_PARTS_MASTER"
set st [oraopen $con]
orasql $st $s
if {[oramsg $st rc] != 0} {
   puts "arere? error? oracode=[oramsg $st rc]"
} else {
   orafetch $st -datavariable row
   puts "fac_parts_master has [lindex $row 0] records."
}
oraclose $st
oralogoff $con

SQL文やPL/SQLブロックを実行させるときには、「文ハンドル」(Statement Handle) を開く必要があります。 これはファイルを開くような感じで、最初にoraopen コマンドで開き、SQL文などを実行させます。終了したら、 oracloseコマンドでカーソルを閉じます。 「おら、オープン」「おら、クローズ」と覚えましょう。(まだ言ってる) で、超基本は上のように、 orasqlコマンドでSQL文を実行し、 orafetchコマンドで結果を取得します。 これらのコマンドはoraopenが返す「文ハンドル」を引数に取ります。 orafetchのオプション「-datavariable row」は、 フェッチした行データをリストとして変数rowに格納するという意味です。 つまり行の1列目の値は[lindex $row 0]で取り出せるわけです。
これ以外に「-dataarray row」のような指定もでき、こちらはリストではなく配列(連想配列) で行データを格納します。 さらに「-dataarray row -indexbyname」とすると、 列名を要素名とする配列に格納します。 つまり、列名が「VENDOR_NAME」の列の値は、$row(VENDOR_NAME)で取得できます。
で、もしSQL文がエラーの場合はどうやって検知するかというと、 実行後oramsgコマンドに文ハンドルと"rc"を渡し、 値をチェックすることで行います。 この値が0なら正常終了で、正の整数はOracleのエラーメッセージコードに対応します。 例えば戻り値が「911」だとすると、 これは「ORA-00911」、つまり「文字が無効です」エラーが発生したことになります。

ちなみに、[oramsg $st "rows"] には、INSERT、UPDATE、DELETE文のとき、 処理された(追加、更新、削除された)行数が、 またSELECT文の場合はorafetchで今までに取り出された行数が保持されています。

●結果を複数行返すSELECT文
今度は、結果が複数行返ってくるSELECT文です。基本は上と同じく、 orasqlとorafetch を使います。下のようなコードが最も典型的な感じだと思います。

package require Oratcl

set con [oralogon "DEVUSER/DEVPASS@DEVDB"]
set st  [oraopen $con]

set s "SELECT PARTS_CLASS, PARTS_CODE FROM FAC_PARTS_MASTER"
foreach e {"SELF" "BMON12"} {
    # SQL文をTcl文字列として構築にゃ
    set s2 "$s WHERE MAKER_CODE = '$e'"

    # orasqlでSQL文を実行にゃ
    orasql $st $s2

    # 結果を1行ずつ取り出すにゃー
    orafetch $st -datavariable row
    while {[oramsg $st rc] == 0} {
        puts "result: class '[lindex $row 0]' code '[lindex $row 1]' mcode=$e"
        orafetch $st -datavariable row
    }
}
# あとしまつ
oraclose  $st
oralogoff $con

結果が複数行になるときは、 orafetchを繰り返し使って結果を1行ずつ取り出します。 で、その繰り返しをいつやめるかというと、 今度もoramsgコマンドにハンドルと"rc" を渡した際の値が零でなくなったときに、全部の行の取り出しが終了したと判断します。 C言語の「EOFに出会った」ようなものです。 ちなみに、oramsgを使わずもっと短く書ける方法として、 orafetchの戻り値がSQL実行終了ステータスで、これが0以外なら抜けるというのもOKです。

なお、INSERT文やUPDATE文の場合の注意ですが、TclODBCと違い、 ていうかOracleではどちらかというとこちらが普通ですが、 oralogonコマンドで開いたおらおらトランザクションは、 デフォルトではオートコミットではありません。 トランザクションを切断するか、 oracommitコマンドを使うまでコミットは行われません。 また、その間に何か具合が悪くなってロールバックするには、 orarollコマンドを使います。

●PL/SQLブロックの実行
そしてやってまいりました。 Oratclでは、Oracleのストアドプロシージャ記述言語PL/SQLのブロックを Tclスクリプト内で定義し、これを実行させることができます。 PL/SQLは制御構造も備えたプログラミング言語なので、 非常に柔軟な処理をさせることができます。これがODBCにはない利点ですね。

package require Oratcl
set con [oralogon "URANO/AHONDARA@ZMDB"]

set plblock {
    begin
       SELECT count(*) into :n
         FROM XQTE.FAC_PARTS_MASTER
        WHERE maker_code = :mcode;
    end;
}

set cur [oraopen $con]
foreach e {"SELF" "BMON12"} {
    oraplexec $cur $plblock :n "" :mcode $e
    set r [orafetch $cur]
    puts "maker=[lindex $r 1] count=[lindex $r 0]"
}
oraclose $cur
oralogoff $con

PL/SQLブロックを実行させるには、orasql ではなくoraplexecを使います。 その2番目の引数で指定するPL/SQLブロックには、「バインド変数」として コロン「:」で始まる識別子を含めることができます。その後ろの引数で、 順に「バインド変数」とその「値」をこのように交互に指定すると、 Oracleの中でバインド変数が値に置き換えられて処理が行われます。
  oraplexec $cur $plblock :n "" :mcode $e
SELECT INTO文の格納変数など、出力用のバインド変数に対しては、 値は空文字列 "" にしておきます。
oraplexecに対するorafetch は、ブロックの実行が終了した時点での各バインド変数の値を Tclリストとして返してきます。
  oraplexec $cur $plblock :n "" :mcode $e
  set r [orafetch $cur]
この例では、[lindex $r 0] に「:n」の値が、[lindex $r 1]に 「:mcode」の値が入っています。

●単なる計算もできます
先述の通り、PL/SQLはOracle製品に搭載されている事実上のプログラミング言語で、 データベース処理に関係ないただの計算をさせることもできます。

package require Oratcl
set con [oralogon "URANO/AHOUDORI@KEISANDB"]

set pls1 {
    begin
        :n := 3 * 5;
        :str := 'Hello, World.';
    end;
}

set cur [oraopen $con]
oraplexec $cur $pls1 :n "" :str ""
set r [orafetch $cur]
puts "n=[lindex $r 0] str=[lindex $r 1]"

oraclose $cur
oralogoff $con

無論、こんな使い方をする人はいないわけですが、ここでは 「ちょっとデータベースからデータをとってきて、 いろいろ計算してデータを加工することもできるんよー」 という程度のサンプルです。あと、上の :n と :str でわかりますが、バインド変数は変数の「型」を宣言する必要はなく、 いきなり数値か文字列を代入させることができます。

●バインド変数を使ったSQL実行
同じような問い合わせを一部のパラメータ値を変えて何度も行う場合、 パラメータの部分をバインド変数にしたSQL文を使うと、 字句解析の手間が省け、処理が高速になります。

package require Oratcl
set con [oralogon "URANO/FUKUROU@SEIZODB"]

# SQL文。変えたいパラメータ値として:で始まるバインド変数を使用
set s "SELECT parts_class, parts_code
         FROM XQTE.FAC_PARTS_MASTER
        WHERE maker_code = :mcode"

set cur [oraopen $con]
# 字句解析だけ先に行う
orasql $cur $s -parseonly
foreach e {"SELF" "BMON12"} {

    # ここでバインド変数 :mcode に値 $e をセットし、SQL実行
    orabindexec $cur :mcode $e

    # 結果はもうおなじみorafetchで取り出します。
    set r [orafetch $cur]
    while {$oramsg(rc) == 0} {
        puts "class='[lindex $r 0]' code='[lindex $r 1]' mcode=$e"
        set r [orafetch $cur]
    }
}

oraclose $cur
oralogoff $con

まずorasqlコマンドを-parseonlyスイッチをつけて実行し、 指定したSQL文の字句解析をさせておきます。 で、ループ内では orabindexecコマンドを使い、 毎回バインド変数の値だけを差し替えて実際に問い合わせを実行します。 結果はおなじみorafetchで取り出すことができます。

●小さなツール向き
Oratclに限らず、プログラムでデータベース管理ツールを作る際には、 マイクロソフトのAccessのようなワークシート状のデータ操作を提供するのは困難です。 しかし下のようなマスターメンテナンスなど小さな管理ツールには向いています。 特に非常に記述力が高いTcl/Tk+Oratclでは簡単に手早くこのようなGUIを作ることができます。 ちなみに下のツールのTclプログラムはわずか150行。 ソースプログラムはこちらです。

クリックすると拡大できます

●というわけで
…というわけで、Oratclを使うとOracleデータベースに接続してちょっとデータを表示したり加工したりするようなおらおらGUIツールが簡単に作れます。 仕様書を書いてスケジュールを決めて、というような大規模なシステムでなければ、 Tcl/TkによるRADがこのようなツールの開発にはとても有効です。 またOracleのSQL*PlusやSQL*Loader、exp&impなどの文字ベースのおらおらツールを Tcl/Tkから呼び出すようなツールも考えられるでしょう。

  おらくるなび

 
↑Oracleの付属ツールおらくるなび(Oracle8 Navigator) の画面。このようなデータ編集用のGUIツールもありますが、 エンドユーザーが使うにはやはり敷居が高いのも事実。 こんなときこそ小回りの効くツールを作れば、 総務のSさんのあなたを見る目も変わるでしょう。(変わらない変わらない)

拡張レビュー分室 top
(first uploaded 2000/10/09 last updated 2012/05/27, MISUMI URANO)