[Delphi:89754] 半角を全角

1,371 views
Skip to first unread message

ron

unread,
Jul 17, 2007, 10:55:31 PM7/17/07
to Del...@ml.users.gr.jp
こんにちは、ろんです。

半角と全角の混ざった文字列の半角文字をすべて全角にしたいと考えています。
それで、ネットを参考に以下のようなコードを書いてみました。

var
Chr : array[0..2048] of char;
strTemp : string;

begin
strTemp := 'ほえほえhoehoe';

Windows.LCMapString(
GetUserDefaultLCID(),
LCMAP_FULLWIDTH,
PChar(strTemp),
Length(strTemp) + 1,
Chr,
Sizeof(Chr)
);

Mojiretu := Chr;
end;

http://www.wwlnk.com/boheme/delphi/tips/tec1310.htm

ほとんど上のページの丸写しなのですが、

PChar(strTemp)

で、「正しくない型キャスト」のエラーがでます。

開発環境は、WinXP SP2 + Delphi2006(.NETモード)です。

よろしくお願いします。


--
ron
mailto:r...@ebetsu.arrow.jp

Mr. Kazutaka Morono

unread,
Jul 17, 2007, 11:36:44 PM7/17/07
to Del...@ml.users.gr.jp
諸農です。

ron <r...@ebetsu.arrow.jp>さん:


> 開発環境は、WinXP SP2 + Delphi2006(.NETモード)です。


最近はDelphiを全然触っていないので見当違いかも知れませんが、
「.NETモード」というのが単純に.NETのライブラリを使えるという
意味なのであれば、Microsoft.VisualBasic 名前空間にある
Strings.StrConv()を使うってのはどうでしょうか。

■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■
◇ 諸農和岳 <k-mo...@mbm.nifty.com>
■ Kazutaka Morono - Osaka , Japan

■ Microsoft MVP for Visual Developer C# (Oct 2004 - Sept 2007)
◇ with VAIO PCG-X505/SP EXTREME
■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■

ron

unread,
Jul 18, 2007, 1:25:28 AM7/18/07
to Del...@ml.users.gr.jp
こんにちは、ろんです。

Mr. Kazutaka Morono wrote (2007/07/18 12:36):

> 最近はDelphiを全然触っていないので見当違いかも知れませんが、
> 「.NETモード」というのが単純に.NETのライブラリを使えるという
> 意味なのであれば、Microsoft.VisualBasic 名前空間にある
> Strings.StrConv()を使うってのはどうでしょうか。

http://faq.blackcatlab.com/archives/2005/03/del0055.htm

こんな感じでうまくいきました。あと、usesのところにMicrosoft.VisualBasic
と書かないとうまくいかない感じです。

こうなると.NETは非常に便利に思えてきます。早くDelphiも3.5とかに対応して
欲しいです。

ついでにLinuxのMonoにも対応してくれると言うことなしかもです。(^^;;;

ありがとうございました。m(_ _;)m


--
ron
mailto:r...@ebetsu.arrow.jp

天野 潔

unread,
Jul 18, 2007, 8:59:28 PM7/18/07
to Del...@ml.users.gr.jp
天野です。

「正しくない型キャスト」は再現できないのでよく分かりませんが、以下のコー
ドで問題なく変換できました。
なお、サンプルのように char の配列宣言するのは配列数に汎用性がないのであ
まりよくないと思います。文字列の長さに合わせてメモリを確保・開放するのが
よいでしょう。

function HanToZen(str:string):string;
var
strw: string;
c1: PChar;
begin
c1 := PChar(GlobalAlloc(GPTR, Length(str) * 2+1));

LCMapString(GetUserDefaultLCID(),
LCMAP_FULLWIDTH,
PChar(str),
-1,
c1,
Length(str)*2);
strw := c1;
GlobalFree(HGLOBAL(c1));
Result := strw;
end;

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
天野 潔 <kiyosh...@nifty.com>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

天野 潔

unread,
Jul 18, 2007, 11:00:12 PM7/18/07
to Del...@ml.users.gr.jp
天野です。

早とちりしてすみません。.NET モードでしたね。
以下でうまくいきました。
.NET からネイティブ関数にアクセスする場合、PChar ではなく StringBuilder
でよいはずです。
4倍しているのは utf8 では 半角→全角 変換のバイト数が最大4倍らなるた
めです。

function HanToZen(str:string):string;
var

sb: System.Text.StringBuilder;
ret: Integer;
begin
sb := System.Text.StringBuilder.Create();
sb.Append(str + str + str + str + ' ');

ret := Windows.LCMapString(
GetUserDefaultLCID(),
LCMAP_FULLWIDTH,
str,
Length(str) * 4 + 1,
sb,
Length(str) * 4 + 1
);

Result := sb.ToString();
end;
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
天野 潔 <kiyosh...@nifty.com>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ron

unread,
Jul 19, 2007, 3:30:33 AM7/19/07
to Del...@ml.users.gr.jp
こんにちは、ろんです。

天野 潔 wrote (2007/07/19 12:00):

> .NET からネイティブ関数にアクセスする場合、PChar ではなく StringBuilder
> でよいはずです。
> 4倍しているのは utf8 では 半角→全角 変換のバイト数が最大4倍らなるた
> めです。

わざわざありがとうございます。m(_ _;)m

コード的には諸農さんの方法で解決させていただきました。

DelphiのヘルプにVBの話が載ってるのが前々から不思議だったのですが、こうい
うことだったんですねぇ。


--
ron
mailto:r...@ebetsu.arrow.jp

tonbo_ik

unread,
Jul 24, 2007, 2:22:29 AM7/24/07
to Del...@ml.users.gr.jp
あくつです。

このMLのお陰で、1から始めてシステム稼動まで漕ぎ着けることが
できました。
感謝しております。

実は、DBを数台の子機から書き換える時に、
衝突エラー「deadlock: update conflicts with concurrent update」
が出ることがあります。
その時は、アプリを立ち上げ直します。
頻度はごくまれで、DBも壊れていないため放置しています・・・

Q1.エラー発生の時に「try~except」等で、確実にrollback
(IBXではCancel?)を行う方法はあるのでしょうか。
あるとすれば、具体的な方法をお教え頂けますか。

Q2.時間指定のできない「Wait」で10秒くらい待たせる方法は
ありますでしょうか。

【環境】
・Delphi7(Pro)付属のIBX(InterBase Express) + FireBird1.5(InterBase6互換)
+ Windows2000を使用しています。

・親機・子機とも、TIBDatabase, TIBTransaction, TDataSource,
TIBDataSetを貼り付けています。

・親機・子機とも、IBTransactionのParamesプロパティは、
read_committed
rec_version
wait <-時間指定できない?
としています。

ご指導よろしくお願いいたします。

中村 隆

unread,
Jul 24, 2007, 10:24:59 PM7/24/07
to Del...@ml.users.gr.jp
当方FireBird1は利用していないのですが、ORACELは以下のような関数を作成
してLOCKを利用しています

使用方法は
CsCowLockDenNo( 'KEY', StatusBarGuid, WrkKey ); ←Lock
    ↓
Inc( WrkKey );
    ↓
CsCowUnLockDenNo( 'KEY', WrkKey ) ; ←UnLock

質問の意味を取り違えていましたら、ごめんなさい

//------------------------------------------------------------------//
// トランファイル書込用KEY値取得
//------------------------------------------------------------------//
// 注意:
// オラクルのバグ、Lockする場合は必ず トランザクション処理を行う必要があるしないと2回目以降エラー発生
//------------------------------------------------------------------//
// 使用例:
// CsCowLockDenNo( 'TKEYPRN', StatusBarGuid, WrkKey ); // コントロールマスターへも生産量を更新
//------------------------------------------------------------------//
function CsCowLockDenNo( ArgDenName : String; ArgComp : TComponent; var ArgDenNo : Integer ) : Boolean;
const
CnsWaitTime1 = 20/24/60/60; // 書き込み処理待ち時間(20秒)
// CnsWaitTime2 = 20/24/60/60; // 書き込み処理待ち時間(5分)
CnsWaitTime2 = 1/24/60; // 書き込み処理待ち時間(1分)
var
WrkSql, WrkStr : String;
WrkDateTimeCk1 : TDateTime;
WrkDateTimeCk2 : TDateTime;
WrkDateTimeCk3 : TDateTime;
SwtErr : Boolean;
begin
WrkSql := 'SELECT MDEN_NO FROM MDEN WHERE ( MDEN_FID = '
+ Cs_OraSqlAddSepa( CnsOraChar, ArgDenName )
+ ' ) FOR UPDATE NOWAIT';

DMInput.QADODenNo.Close;
DMInput.QADODenNo.SQL.Clear; // SQL文の消去
DMInput.QADODenNo.SQL.Add(WrkSql);

WrkDateTimeCk1 := Now;
WrkDateTimeCk2 := WrkDateTimeCk1 + CnsWaitTime1; // 2秒
WrkDateTimeCk3 := WrkDateTimeCk1 + CnsWaitTime2; // 5分

Result := False;
ArgDenNo := 0;
while True do // Start Loop(While)
begin
try
SwtErr := False;
DMInput.QADODenNo.Open;
if DMInput.QADODenNo.Eof = True then
begin
MessageDlg('*E:伝票番号未登録(CsCowLockDenNo:' + ArgDenName + ')',
mtError, [mbYes], 0 );
DMInput.QADODenNo.Close;
exit;
end;
except
SwtErr := True;
end; // End Of Try

if SwtErr = False then
begin
ArgDenNo := DMInput.QADODenNo.FieldByName( 'MDEN_NO' ).AsInteger;
DMInput.QADODenNo.Close;
Result := True;
Exit;
end;

DMInput.QADODenNo.Close;
if WrkDateTimeCk1 < Now then
begin
WrkStr := '*E:他のアプリケーションがレコードにロックをかけています。(' +
FormatDateTime('hh',WrkDateTimeCk1) + ':' +
FormatDateTime('nn',WrkDateTimeCk1) + ':' +
FormatDateTime('ss',WrkDateTimeCk1) + '→' +
FormatDateTime('hh',Now) + ':' +
FormatDateTime('nn',Now) + ':' +
FormatDateTime('ss',Now) + '→' +
FormatDateTime('hh',WrkDateTimeCk3) + ':' +
FormatDateTime('nn',WrkDateTimeCk3) + ':' +
FormatDateTime('ss',WrkDateTimeCk3) + ')。' ;
Cs_ErrMsg( WrkStr, ArgComp );
end;
if WrkDateTimeCk2 < Now then
begin
if MessageDlg('*E:更新処理を中断しますか?',mtError, [mbRetry,MbAbort], 0 )
= mrAbort then
begin
Exit;
end;
WrkDateTimeCk1 := Now;
WrkDateTimeCk2 := WrkDateTimeCk1 + CnsWaitTime1; // 2秒
WrkDateTimeCk3 := WrkDateTimeCk1 + CnsWaitTime2; // 5分
end;
Application.ProcessMessages; // タスク切替え
Sleep(1000);
end; // End Loop(While)end;
end;

//------------------------------------------------------------------//
// トランファイル書込用KEY値書込
//------------------------------------------------------------------//
// 注意:
// オラクルのバグ、Lockする場合は必ず トランザクション処理を行う必要があるしないと2回目以降エラー発生
//------------------------------------------------------------------//
// 使用例:
// CsCowUnLockDenNo( 'TKEYPRN', WrkKey );
//------------------------------------------------------------------//
function CsCowUnLockDenNo( ArgDenName : String; ArgDenNo : Integer ) : Boolean;
var
WrkStr : String;
begin
WrkStr := 'UPDATE MDEN SET MDEN_NO = '
+ IntToStr( ArgDenNo )
+ 'WHERE ( MDEN_FID = '
+ Cs_OraSqlAddSepa( CnsOraChar, ArgDenName )
+ ' )';
DMInput.QADODenNo.Close;
DMInput.QADODenNo.SQL.Clear; // SQL文の消去
DMInput.QADODenNo.SQL.Add(WrkStr);
DMInput.QADODenNo.ExecSQL;
Result := True;
end;

tonbo_ik

unread,
Jul 25, 2007, 6:44:25 AM7/25/07
to Del...@ml.users.gr.jp
中村様

丁寧なご回答をありがとうございました。
FireBirdに当てはめてみます。

理解するのに時間がかかるため、ご報告が遅れますことをお許し下さい。

 あくつ

> 当方FireBird1は利用していないのですが、ORACELは以下のような関数を作成
> してLOCKを利用しています
(以下略)

tonbo_ik

unread,
Jul 27, 2007, 4:40:33 AM7/27/07
to Del...@ml.users.gr.jp
中村様
あくつです。
アドバイスありがとうございました。

以下、私の理解です。違っていましたらご指摘下さい。
・QADODenNoは、IBDataset相当と思います。
・QADODenNoは、必要な時にOpenにいきます。
・OpenできればTrueで終了です。
・Openできない場合、1秒毎に「他のアプリケーションがレコードに
ロックをかけています」のメッセージを出します。
・2秒以上Openできない時は「更新処理を中断しますか?」のMessageDlgで
判断を仰ぎます。

私のプログラムでは、現在、次のようにしています。
・IBDatasetは常にOpenにしています。
・複数の子機から同じDBへ書き換えに行きますが、まれに同じレコード
(の異なるフィールド)に同時に書き換えに行くことがあり得ます。
でも、書き換え量は数十Byteで短時間と思われるため、IBTransactionの
「Wait」で逃げています。(下記私のメール。これで良いか自信ありません)

書き換え箇所はプログラムに分散しており、高速応答もしたいため、
できれば現在のOpenしたままでいきたいと思います。

私のやり方は、まずいでしょうか?
よろしくお願い致します。

Takashi

unread,
Jul 29, 2007, 3:37:33 AM7/29/07
to Del...@ml.users.gr.jp
> ・QADODenNoは、IBDataset相当と思います。

InterBase環境をインストールしていないので、
「IBDataset」はADOコンポーネントで何に相当するのか
確認していませんが、QADODenNoは、TADOQueayです。

> ・QADODenNoは、必要な時にOpenにいきます。
Open=Lockだと考えて下さい、本来SQLの世界ではOPENという概念
は無いと思います。ORACLEでは「FOR UPDATE NOWAIT」付き
の「SELECT」文を実行することで「LOCK」が掛かります。エラー時
はロックがかから無かったので再度実行します。

> 私のプログラムでは、現在、次のようにしています。
更新の基本は
1.ロックする
2.更新
3.ロック解除

これだけです。これだけで更新中エラーは抑えられると思います。
Firebird SQLリファレンス を見ると SELECT の説明で「WITH LOCK」
があるのでこれが利用できるのではないでしょうか


> 書き換え箇所はプログラムに分散しており、高速応答もしたいため、
高速応答とOPENしたままは関係無いと思います。

> 「Wait」で逃げています。(下記私のメール。これで良いか自信ありません)
Waitが何か具体的には解らないのですが、クライアント台数が増えたときや
マシン負荷などによって処理速度は変わってきます。単純な時間を指定する方法
は私だったらしません。確実なロックをお勧めします。

PS
InterBaseではありませんが、Firebird のベンチマーク資料が↓の片岡さん
のセミナー資料としてダウンロードできます。

http://www.postgresql.jp/branch/nagoya/postgresql540d53e45c4b652f90e830bb30df30ca30fc2007-npug2007

速度がどの程度必要かわかりませんが、参考にされるとチューニングの目安に
なると思います。


fwhi...@mb.infoweb.ne.jp

unread,
Jul 29, 2007, 9:47:26 PM7/29/07
to Del...@ml.users.gr.jp
福永です。

deadlock に対して、どのような方針を立てられいるのかが見えないので
意図されている回答になっているかどうかわかりませんが・・・

単純になるべくdeadlock を発生させないようにするのであれば排他レベル
が read_commited なら、オプションは no_rec_version でよろしいのではな
いでしょうか。この設定がデフォルトですし。

wait させるのであればなおさら no_rec_version じゃないと意味がないので
はないかしらん。
他のトランザクションがコミットするのを待機していて、コミットされても結局
rec_versionを確認して「衝突発生」ということになるわけですからね。

それと、deadlock 発生するとアプリを再起動しているということですが、これ
がどういう状況かよくわかりません。try-except で deadlock 検知できてない
のですか?

tonbo_ik

unread,
Jul 30, 2007, 7:02:44 AM7/30/07
to Del...@ml.users.gr.jp
中村様
あくつです。

丁寧なアドバイスをありがとうございます。
私が十分理解できず申し訳なく思っています。

> > ・QADODenNoは、IBDataset相当と思います。
>
> InterBase環境をインストールしていないので、
> 「IBDataset」はADOコンポーネントで何に相当するのか
> 確認していませんが、QADODenNoは、TADOQueayです。

TADOQueayを使ったことがなく、以下、IBDatasetでテストしています。

> > ・QADODenNoは、必要な時にOpenにいきます。
> Open=Lockだと考えて下さい、本来SQLの世界ではOPENという概念
> は無いと思います。ORACLEでは「FOR UPDATE NOWAIT」付き
> の「SELECT」文を実行することで「LOCK」が掛かります。エラー時
> はロックがかから無かったので再度実行します。
>
> > 私のプログラムでは、現在、次のようにしています。
> 更新の基本は
> 1.ロックする
> 2.更新
> 3.ロック解除
>
> これだけです。これだけで更新中エラーは抑えられると思います。
> Firebird SQLリファレンス を見ると SELECT の説明で「WITH LOCK」
> があるのでこれが利用できるのではないでしょうか

1.子機A、B2台でテストしました。
子機AでIBDataset.Openを行った後、子機Bで
Try
IBDataset.Open;
except
SwErr := True;
end;
を行いましたが、正常にOpenされ、exceptへ来ませんでした。

2.子機A 1台で同じテストをしましたが(2重Open)、同じくexceptへ
来ませんでした。

IBDatasetでは、うまく検出できないのでしょうか。

> > 書き換え箇所はプログラムに分散しており、高速応答もしたいため、
> 高速応答とOPENしたままは関係無いと思います。

高頻度でDBを書き換えにいくため、Open/Closeは少ないほうが
早いのでは、と思いました。

> > 「Wait」で逃げています。(下記私のメール。これで良いか自信ありません)
> Waitが何か具体的には解らないのですが、クライアント台数が増えたときや
> マシン負荷などによって処理速度は変わってきます。単純な時間を指定する方法
> は私だったらしません。確実なロックをお勧めします。

続けて、どうしたらロックできるか検討します。

> PS
> InterBaseではありませんが、Firebird のベンチマーク資料が↓の片岡さん
> のセミナー資料としてダウンロードできます。
>
>
http://www.postgresql.jp/branch/nagoya/postgresql540d53e45c4b652f90e830bb30d
f30ca30fc2007-npug2007
>
> 速度がどの程度必要かわかりませんが、参考にされるとチューニングの目安に
> なると思います。

情報ありがとうございました。
マルチクライアントでは、Firebirdは、あまり早くないのですね。

 あくつ

tonbo_ik

unread,
Jul 30, 2007, 7:26:25 AM7/30/07
to Del...@ml.users.gr.jp
福永様
あくつです。ご回答ありがとうございます。

> deadlock に対して、どのような方針を立てられいるのかが見えないので
> 意図されている回答になっているかどうかわかりませんが・・・

十分な回答になっております!

deadlock回避は、IBTransactionのParamesプロパティ頼みです::;
他に回避方法が見つかりませんでした。

> 単純になるべくdeadlock を発生させないようにするのであれば排他レベル
> が read_commited なら、オプションは no_rec_version でよろしいのではな
> いでしょうか。この設定がデフォルトですし。
>
> wait させるのであればなおさら no_rec_version じゃないと意味がないので
> はないかしらん。
> 他のトランザクションがコミットするのを待機していて、コミットされても結局
> rec_versionを確認して「衝突発生」ということになるわけですからね。

ありがとうございました!
no_rec_versionでテストしたところ、完全なdeadlock回避をしているようです。
実は、no_rec_versionをネットで調べましたが意味が分からず、色々トライして
rec_versionまでたどり着きました・・・

> それと、deadlock 発生するとアプリを再起動しているということですが、これ
> がどういう状況かよくわかりません。try-except で deadlock 検知できてない
> のですか?

try-exceptでdeadlockを検知する方法を教えて頂けないでしょうか。
これも色々調べたのですが、未だに方法が見つかっていません。

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

fwhi...@mb.infoweb.ne.jp

unread,
Jul 30, 2007, 9:30:16 PM7/30/07
to Del...@ml.users.gr.jp
福永です。私が利用しているのはInterBase5.5ですので、違うとこ
ろがあるかも知れませんが・・・

>実は、no_rec_versionをネットで調べましたが意味が分からず、
>色々トライしてrec_versionまでたどり着きました・・・

トランザクションの排他レベルはsnapshotとread_commited があり
(snapshot table stability もあるが省略)、read_commited はト
ランザクション開始後も他のトランザクションが更新してコミット
したレコードを読むことができるが、snapshot ではそのトランザ
クション開始時のビューが維持されるため他のトランザクションが
レコードを更新しても、その更新前のレコードが読み取られ、それ
らの他トランザクションが更新したレコードへの更新は禁止されま
す(deadlockが発生する)。InterBaseではsnapshotがデフォルト
の排他レベルになっています。

read_commitedを利用する場合はrec_version と no_rec_versionパ
ラメータによってトランザクションがコミットされていないレコー
ドにアクセスした場合の動作を指定することができます。
rec_versionは(コミットされたいない最新のレコードが存在する
場合でも)最後にコミットされたレコードに対して読み取りが行わ
れ、no_rec_versionではコミットされていなくても最新の状態のレ
コードに対して読み取りが実行されます。ただしno_rec_versionの
場合はwait を指定していると、いったん待機状態に入り、コミッ
トされていないレコードがコミット(またはロールバック)されて
から再読込みを実行します。no_waitなら即座にdeadlock を発生さ
せます。

もっともdeadlockが発生しにくいのは read_commited, wait,
no_rec_versionの組み合わせとなりますが、read_commitedによる
様々な副作用を考慮してアプリケーションを設計する必要があり、
アプリケーションに対する業務要件などを踏まえた排他制御の方
針をはっきりさせておくことをお勧めします。 read_commitedで
は更新不能読み取り、再現不能読み取り、ファントム行などの副
作用が発生しえるので、そのような副作用を極力起こさないよう
にアプリ側でのロジックや、発生した場合のエンドユーザーの処
理手順などを明確にしておく必要があると思います。

>try-exceptでdeadlockを検知する方法を教えて頂けないでしょう
>か。これも色々調べたのですが、未だに方法が見つかっていません。

普通にdelphiのマニュアルにも記載されていますけれども、簡単に
書けば

procedure TForm1.btnCommitClick(Sender: TObject);
begin
if not IBDatabase1.Connected then
IBDatabase1.Open;

if not IBTransaction1.InTransaction then
IBTransaction1.StartTransaction;

try
IBSQL_Update.ExecQuery; //UPDATE文の実行
IBTransaction1.Commit;
except
IBTransaction1.Rollback;
end;
end;

更新処理でdeadlockなどが発生すると例外が生成されますので、コ
ミットされずにexcept句でロールバックされます。

またInterBaseのトランザクションの関しては、こもメーリングリス
トの過去ログでも有識者の方々が有益な情報を提供してくださって
いるので検索することをお勧めします

http://leed.issp.u-tokyo.ac.jp/‾takeuchi/delphi/nsearch.cgi?key=read+commit&ba
ck=http%3A%2F%2Fleed%2Eissp%2Eu%2Dtokyo%2Eac%2Ejp%2F%7Etakeuchi%2Fdelphi%2Fnre
sult%2Ecgi%3Fkey%3Dread%2Bcommit

tonbo_ik

unread,
Jul 31, 2007, 7:47:23 AM7/31/07
to Del...@ml.users.gr.jp
福永様
あくつです。詳しい解説をありがとうございました。

> トランザクションの排他レベルはsnapshotとread_commited があり
> (snapshot table stability もあるが省略)、read_commited はト
> ランザクション開始後も他のトランザクションが更新してコミット
> したレコードを読むことができるが、snapshot ではそのトランザ
> クション開始時のビューが維持されるため他のトランザクションが
> レコードを更新しても、その更新前のレコードが読み取られ、それ
> らの他トランザクションが更新したレコードへの更新は禁止されま
> す(deadlockが発生する)。InterBaseではsnapshotがデフォルト
> の排他レベルになっています。

ここまで私もたどり着くことができました。

> read_commitedを利用する場合はrec_version と no_rec_versionパ
> ラメータによってトランザクションがコミットされていないレコー
> ドにアクセスした場合の動作を指定することができます。
> rec_versionは(コミットされたいない最新のレコードが存在する
> 場合でも)最後にコミットされたレコードに対して読み取りが行わ
> れ、no_rec_versionではコミットされていなくても最新の状態のレ
> コードに対して読み取りが実行されます。ただしno_rec_versionの
> 場合はwait を指定していると、いったん待機状態に入り、コミッ
> トされていないレコードがコミット(またはロールバック)されて
> から再読込みを実行します。no_waitなら即座にdeadlock を発生さ
> せます。

no_rec_versionとwaitの関係が理解できず、色々な組み合わせで
実際にテストして、read_commited, wait,rec_version が正解なのか、
と思った次第です。

> もっともdeadlockが発生しにくいのは read_commited, wait,
> no_rec_versionの組み合わせとなりますが、read_commitedによる
> 様々な副作用を考慮してアプリケーションを設計する必要があり、
> アプリケーションに対する業務要件などを踏まえた排他制御の方
> 針をはっきりさせておくことをお勧めします。 read_commitedで
> は更新不能読み取り、再現不能読み取り、ファントム行などの副
> 作用が発生しえるので、そのような副作用を極力起こさないよう
> にアプリ側でのロジックや、発生した場合のエンドユーザーの処
> 理手順などを明確にしておく必要があると思います。

no_rec_versionに変えたところ、強力な衝突回避が確認できました!
基本的な内容を、やっと理解できました。
アドバイスがなければ、rec_versionでいくところでした。

> 普通にdelphiのマニュアルにも記載されていますけれども、簡単に
> 書けば
>
> procedure TForm1.btnCommitClick(Sender: TObject); begin
> if not IBDatabase1.Connected then
> IBDatabase1.Open;
>
> if not IBTransaction1.InTransaction then
> IBTransaction1.StartTransaction;
>
> try
> IBSQL_Update.ExecQuery; //UPDATE文の実行
> IBTransaction1.Commit;
> except
> IBTransaction1.Rollback;
> end;
> end;
>
> 更新処理でdeadlockなどが発生すると例外が生成されますので、コ
> ミットされずにexcept句でロールバックされます。

Helpを見直したところ、IBTrnsactionの項に、
「アプリケーションがCommit を呼び出したときにアクティブな
トランザクションがない場合には,例外が生成されます」
と書かれていました;;;

try
IBTransaction1.Commit;
except
IBTransaction1.Rollback;
end;
を、プログラム内にちりばめた方が良いですね。

ただ、no_rec_versionでは、実際はdeadlockが(殆ど?)なさそうなので、
try~exceptは不要、ということはないでしょうか?

> またInterBaseのトランザクションの関しては、こもメーリングリス
> トの過去ログでも有識者の方々が有益な情報を提供してくださって
> いるので検索することをお勧めします

活発な意見交換がされているのですね。
今後、過去ログをまず検索するようにします。
情報ありがとうございました。m@m

 あくつ

戸田 英夫

unread,
Jul 31, 2007, 7:53:28 AM7/31/07
to Del...@ml.users.gr.jp
戸田@お仕事中です

----- Original Message -----
From: "tonbo_ik" <tonb...@ybb.ne.jp>
To: <Del...@ml.users.gr.jp>
Sent: Tuesday, July 31, 2007 8:47 PM
Subject: [Delphi:89772] Re: 衝突エラー対応方法


> try
> IBTransaction1.Commit;
> except
> IBTransaction1.Rollback;
> end;
> を、プログラム内にちりばめた方が良いですね。
>
> ただ、no_rec_versionでは、実際はdeadlockが(殆ど?)なさそうなので、
> try~exceptは不要、ということはないでしょうか?

回線異常やTCP/IP異常など、エラーは想定外で発生する可能性もありますよ!

大事な部分は Rollback を考えておいた方が無難です。

---- (-_-)(-_-)(-_-) THE REAL PROGRAMMER (-_-)(-_-)(-_-) ----
At the beach, The Real Programmer is the one drawing flowcharts in the sand.
戸田 英夫 mailto: hideo...@ntt-neo.co.jp


Takashi

unread,
Jul 31, 2007, 1:36:19 PM7/31/07
to Del...@ml.users.gr.jp
自宅PCに環境をインストールして実験してみました。

当然ですが、ORACLE と同じ文法ではエラーになりました。

以下の文では結果が帰ってきません。
SELECT MDEN_ITEMNO FROM MDEN WHERE ( MDEN_FID = 'A' ) FOR UPDATE

以下の文では文法エラーで怒られてしまいました。
SELECT MDEN_ITEMNO FROM MDEN WHERE ( MDEN_FID = 'A' ) WITH LOCK

Delphiの問題以前にInterBaseの勉強を先にしないと駄目でした。
今日はもう寝ます。


------------------------
Takashi <taka...@csjpn.com>
------------------------


tonbo_ik

unread,
Jul 31, 2007, 9:37:25 PM7/31/07
to Del...@ml.users.gr.jp
戸田様
あくつです。ご指摘ありがとうございます。

> > ただ、no_rec_versionでは、実際はdeadlockが(殆ど?)なさそうなので、
> > try~exceptは不要、ということはないでしょうか?
>
> 回線異常やTCP/IP異常など、エラーは想定外で発生する可能性もありますよ!
>
> 大事な部分は Rollback を考えておいた方が無難です。

おっしゃる通りです。そのように致します。

私の現状の例です。

IBDatasetl.Edit;
IBDatasetl.FieldByName('CODE').AsInteger := 100;
IBDatasetl.Refresh;
IBTransaction1.CommitRetaining;
IBDatasetl.Close; //DBGrid表示更新
IBDatasetl.Open;

これを、次のように変更します。

IBDatasetl.Edit;
IBDatasetl.FieldByName('CODE').AsInteger := 100;
IBDatasetl.Refresh;
try
IBTransaction1.CommitRetaining;
except
IBTransaction1.RollbackRetaining;
MessageDlg('データベースの更新に失敗しました。',mtInformation,[mbOK],0);
end;
IBDatasetl.Close;
IBDatasetl.Open;

IBTransaction1.Commit;の場合は、IBTransaction1.Rollback;
で受けます。

エラー発生確認ができないため、初歩的な間違いがありましたら、
ご指摘頂ければ幸いです。

本題から外れますが、DBGrid表示更新のため、上記のように
Close,Openをしています。
当然、カレントレコードが移動します。それで困る場合は、
カレントレコードの再設定をしています。
もっと良い方法があればお教え下さい。

ありがとうございました。

あくつ

tonbo_ik

unread,
Jul 31, 2007, 9:46:13 PM7/31/07
to Del...@ml.users.gr.jp
中村様
あくつです。
わざわざ実験をありがとうございました。
お手数おかけしまして、申し訳ありません。

中村 隆

unread,
Aug 8, 2007, 7:16:42 AM8/8/07
to Del...@ml.users.gr.jp
> > 以下の文では結果が帰ってきません。
> > SELECT MDEN_ITEMNO FROM MDEN WHERE ( MDEN_FID = 'A' ) FOR UPDATE

その後もしぶとく色々と試してみましたが
結局 TIBQuery では成功しませんでした。 (;_;)

InterBaseのマニュアルを読むとカーソルを利用して自分で
FETCHするような事が書いてありましたが結局できませんでした。

インターネットで色々と検索もしてみました

http://www.interbase-world.com/en/articles/805.php

に関係ありそうな事が書いてあるようですが、消化できませんでした。

横から余分なレスを付けて申し訳ありませんでした。


tonbo_ik

unread,
Aug 9, 2007, 10:54:49 PM8/9/07
to Del...@ml.users.gr.jp
中村様
あくつです。
重ねての調査、お手数おかけします。

私は、次の組み合わせでテストをしてみました。

・IBTransactionのParames : read_commited, wait, no_rec_version

・エラー処理:


try
IBTransaction1.CommitRetaining;
except
IBTransaction1.RollbackRetaining;
MessageDlg('データベースの更新に失敗しました。',mtInformation,[mbOK],0);
end;

・子機の処理:フィールド間で演算し結果をフィールドに格納。
これを全レコードに対して行う。

子機2台同時にスタートさせましたが、1台は、もう1台が終わるのを
待って、処理が始まりました。エラーも発生せず、「正常」終了しました。
しかし、データベースは更新されませんでした・・・
1台ずつ実行すれば、きちんと更新されます。

上記のTransaction設定では、衝突があれば、裏で自動的に回避
(Rollback)され、エラーが発生しないのでは、と想像しました。

実際の作業では、2台同時にこのようなことはしないので、
私の場合はOKです。

お礼かたがた報告まで。

Reply all
Reply to author
Forward
0 new messages