SQLのBLOB型へ画像データを登録更新したいのですが

解決


yTake  2013-08-21 19:20:43  No: 45117

yTakeです。今回はSQLでBLOB型データの更新方法について、質問させて下さい。

既に登録されているレコード上のBLOB型データへ画像を登録・更新する事を考えます。
単純に、UPDATE分で、WHERE句でレコードを限定した上で、BLOB型のフィールドに画像データファイル名を渡すだけではダメでした。

当Q&Aの過去や他の参考サイトを参照して、下記2案を考えましたが、いずれもうまく行っていません。

案1:
    bmp       : TBitMap;
    stream    : TStream;

    stream  :=  IBQuery2.CreateBlobStream( IBQuery2.FieldByName( 'image' ), bmWrite );
    bmp :=  TBitMap.Create();
とした上で、

    sql_cmd :=  'SELECT * from TBL where ID = ''' + id + ''' and NAME = ''' + name + '''';

    IBQuery2.SQL.Text :=  sql_cmd;
    IBQuery2.Open();

    if IBQuery2.IsEmpty = True then
        Showmessage( 'Empty' )
    else
    begin
        cnt :=  0;
        while IBQuery2.Eof = False do
        begin
            IBQuery2.Edit();
            bmp.LoadFromFile( portrait_file );
            bmp.SaveToStream( stream );
            IBQuery2.Post();
            inc( cnt );
        end;
    end;

としてみましたが、access violationです。但しこれは、SELECT文の応答が無限ループになってしまっている為の様です。このSELECT文自体は、他の箇所で用いている場合と全く同じで、正しくSELECT出来ています。なぜ、ここではその応答が無限ループになってしまうのでしょう?他のやり方でレコードを限定するのでしょうか?

案2:
    bmp       : TBitMap;
    blobStream  : TIBBlobStream;

    blobStream  :=  TIBBlobStream.Create();
    blobStream.Database :=  IBDatabase2;
//    blobStream.                         ここで、テーブルを指定したい
    blobStream.FieldAddress( 'PORTRAIT' );

とした上で、
    if IBQuery2.IsEmpty = True then
        ShowMessage( 'Empty' )
    else
    begin
        cnt :=  0;
        while IBQuery2.Eof = False do
        begin
            IBQuery2.Edit();
            bmp.LoadFromFile( portrait_file );
            bmp.SaveToStream( blobStream );
            IBQuery2.Post();
        end;
    end;
とすれば良いのかな?と思いますが、blobStreamのCreate時点で引数が何もないので、データベースやテーブルの指定ができません。その後で個別に設定しようと思いましたが、テーブルの指定が分かりません。
ここでも、案1の様なSELECT分の応答の無限ループ問題が発生するかもしれませんが、、、

インターネット上の例とは、なぜか引数の指定が違っているので、苦慮しています。

いずれの案も"try finally"節で、ストリームを開放しています。

いずれがより良いのか?又、エラーの回避方法に何か示唆を頂ければ幸いです。

環境は、
Windows7 (bootcamp), 
Embedded Firebird v2.5, DELPHI XE3 + IBX,
です。

よろしくお願い致します。


au  2013-08-21 20:56:07  No: 45118

普通にUPDATE分を使ってBLOBフィールの更新部分はパラメータクエリにしたら良いんじゃ無いんでしょうか?

UPDATE TABLE1 SET BLOB1=:BLOB WHERE ID=:ID;
IBQuery1.ParamByName('BLOB').LoadFromFile(File)
IBQuery1.ParamByName('ID').AsInteger := ID;


yTake  2013-08-22 18:08:00  No: 45119

auさん
ありがとうございます。

Replyが遅くなりすみません。

そうですか。UPDATE文でBLOB型データも更新可能と言う事ですね。

すると、上記ですとUPDATE文のすぐ後で"IBQuery1.ParamByName('BLOB').LoadFromFile(File)"をじっこうしていますが、どういう意味でしょうか?

また、根本的な事とは思いますが、UPDATE文のなかで':'が使われています。':'の意味は何でしょうか?
私は使っていませんが、これまではそれなりに期待通りに操作できています。
調べてみましたが、明確に解説してある物を見つけられていません。

ご案内の通り試してみたつもりですが、EDataBaseError(パラメータ'PORTRAIT'が見つかりません)が発生します。
.ParamByName('フィールド名')と.FieldByName('フィールド名')、の違いも良く分かっていません。事実、"ParamByName"と"FieldByName"を入れ替えてみると、"FieldBuName"ではLoadFromFileが出てきません。

試してみたソースは次の通りです。

sql_cmd :=  'UPDATE PATIENT_TBL SET PORTRAIT  = ''' + portrait_file + ''' where ID = ''' + id + ''' and NAME = ''' + name + '''';
IBQuery2.SQL.Text :=  sql_cmd;
IBQuery2.ExecSQL();
IBQuery2.ParamByName( 'PORTRAIT' ).LoadFromFile( portrait_file, ftGraphic );

PORTRAITがデータベース上のBLOB型フィード
ID, NAMEは、DELPHI上の変数で、データベース上から検索すべきIDとNAMEを保持しています。

よろしくお願いします。


DEKO  2013-08-22 18:35:14  No: 45120

> また、根本的な事とは思いますが、UPDATE文のなかで':'が使われています。':'の意味は何でしょうか?
> 私は使っていませんが、これまではそれなりに期待通りに操作できています。
> 調べてみましたが、明確に解説してある物を見つけられていません。

[SQL インジェクション]
http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=1162

[IBConsole 日本語版+α ユーザーズガイド]
http://ht-deko.minim.ne.jp/software/ibconsole_ja_mod.pdf


yTake  2013-08-22 20:21:38  No: 45121

DEKOさん
たいへんありがとうございました。

auさんにりぷらい頂いた時から、”パラメータクエリ”を正しく認識できていませんでした。”SQLインジェクション”も初めて聞きました。勉強不足です。
パラメータクエリの趣旨が良く分かりました。

資料を参照させえて頂き下記の様に修正しました。パラメータは認識されたのですが、次の問題が出現しています。

sql_cmd :=  'UPDATE PATIENT_TBL SET PORTRAIT = :PORTRAIT WHERE ID = ''' + id + ''' and NAME = ''' + name + '''';
IBQuery2.SQL.Text :=  sql_cmd;
IBQuery2.ParamByName( 'PORTRAIT' ).LoadFromFile( portrait_file, ftGraphic );
IBQuery2.ExecSQL();

これを実行すると、"EIBClientErrorサポートされていない機能"が発生します。
これはどういうことでしょう?FlameRobinやIBConsoleなどでは、BLOB型へ画像を登録し参照する事は出来ています。

もしかして、デバッグ中はSuper Serverへ接続するはずが、Embedded Serverへ接続されているのか?とも思いましたが、IBDatabaseのDatabaseNameプロパティを表示させていますが、リモート接続を意味する"localhost:"で始まっています。

database  :=  'データベース本体へのパス';
{$IFDEF DEBUG}
database  :=  'localhost:' + database;
{$ENDIF}
IBDatabase2.DatabaseName :=  database;

の様にコーディングしています。
ビルド時に、ビルド構成を"Debug"と"Release"とを選択切り替えする様にしています。サーバー接続の切り替えがうまくいっていないのでしょうか?

だとしても、Embedded Serverでサポートされていないのも困ってしまいますが、、、、

よろしくお願いします。


yTake  2013-08-22 22:09:21  No: 45122

分かりました。

"LoadFromFile"の第2引数を、過去の例から倣って"ftBlob"に変更してみたら、正常に動作しました。
IBQuery2.ParamByName( 'PORTRAIT' ).LoadFromFile( portrait_file, ftBlob );

お騒がせ致しました。
良く理解しないで使うと思わぬ迷宮に入ってしまいますね。

皆様、今回も有益なご教授をありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加