参考になった!役立った!・・・・そのときは一言メールお願いします!
Oracle ヒント
DATETIME型の変数(列)に日数を足す(引く)ときはDT := DT + 1; /* 1日進める */のようにします。
DT := DT - 5; /* 5日戻す */さらに時間単位で足し引きしたいときは
DT := DT + 1/24; /* 1時間進める */のようにします。
DT := DT - 10/24/60; /* 10分戻す */
Oracle8(もしかしたら、Oracle7の高バージョン)ではプロシージャからDLLを呼ぶことができます。
この機能を利用すれば、当然WindowsAPIだって呼べちゃいます。ここでは、Iniファイルを読むAPIを使ってみます。
Iniファイルを読む関数としてはGetPrivateProfileIntA、GetPrivateProfileStringAがあります。
VBのAPIビューアを見ると、それぞれ以下のように定義されています。
Declare Function GetPrivateProfileInt Lib "kernel32" Alias "GetPrivateProfileIntA"
(ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal nDefault As Long, ByVal lpFileName As String) As LongDeclare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA"
(ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
この情報を利用してPL/SQLの外部プロシージャを定義します。--↓ここからプログラム開始関数名、引数名は好きなものを付けられます。引数の並びはAPIに合わせて下さい。-- DLLファイルの名称定義 ディレクトリは環境によって変えてください
create or replace library kernel32 is 'c:\winnt\system32\kernel32.dll';
show errors-- APIのGetPrivateProfileIntA(Iniファイルから数値を取り出す)
create or replace function GetIniInt(
lpApplicationName CHAR,
lpKeyName CHAR,
nDefault BINARY_INTEGER,
lpFileName CHAR
) RETURN BINARY_INTEGER AS
EXTERNAL
LIBRARY kernel32
NAME "GetPrivateProfileIntA"
LANGUAGE C
CALLING STANDARD C;
/
show errors-- APIのGetPrivateProfileStringA(Iniファイルから文字列を取り出す)
create or replace function GetIniString( --この関数名は好きなものを付けられます
lpApplicationName String,
lpKeyName String,
lpDefault String,
lpReturnedString OUT String,
nSize BINARY_INTEGER,
lpFileName String
) RETURN BINARY_INTEGER AS
EXTERNAL
LIBRARY kernel32
NAME "GetPrivateProfileStringA"
LANGUAGE C
CALLING STANDARD C;
/
show errors
--↑ここまでプログラム
また、VBの定義でlongだった部分は、BINARY_INTEGERを使用してみました。LONG型はOracleでは2Gbyteまで扱える文字変数なので気を付けましょう。と、いうか、私は見事に引っかかりました・・(^-^;Aついでに呼び出しのスクリプトの例も記述しておきます。
set serveroutput on
declare
dat varchar2(255);
ret int;
beginret := GetIniInt('intl','iCountry', 0, 'c:\winnt\win.ini');
dbms_output.put_line(ret);ret := GetIniString('intl','sCountry', 'Default', dat, 255, 'c:\winnt\win.ini');
dbms_output.put_line(dat || ' ' || ret);end;
Oracleのインストール完了直前にインストーラーが異常終了する事があります。
どうやら、Y2K関係の問題らしいです。
これを回避するには、オラクルのHPからY2K対策済みのインストーラーをDLして使用する必要があります。
ProC/C++で作成したどー考えても単純なSQL文が原因不明のエラーを出すときは、ProCのpreファイル(メークファイルみたいなもの??)を 新規に作成しなおしてみましょう。特に、Oracle7で作成したpreファイルをOracle8にバージョンアップするときは必ず新規で作った方が無難です。 私はこれで、3時間潰しました・・・(泣)
あっ、落ちるっていっても、異常終了じゃないですよ〜
(現象が起きたのはOracle8.0.5でした)
作成したSQL文が実際にDBエンジンでどんな風に解釈され検索されるか・・・・これって以外と解らないものです。
単純なSQL文であればそれほど問題にはなりませんが、大量データの、しかも複数のテーブルを連結している場合はどんな順序でテーブルが検索されているか解るとパフォーマンス改善に役立ちます。
そんな時にはSQL文の実行計画を取得すると一目瞭然です。
詳しくはオラクル社のHPの技術資料(アプリケーション開発資料)にPDFドキュメントとしてUPされています。
ここでは簡単に説明したいと思います。@ SQL*Plus、OracleSQLWorksheet等で実行計画を取得したいユーザーにログイン。
utlxplan.sqlスクリプトを実行し実行計画を取得したいユーザー毎にPLAN_TABLE表を作成する。(初回のみ)SQL>@%ORACLE_HOME%\RDBMS80\ADMIN\UTLXPLAN.SQLA 以下のスクリプトを実行する。
(下線部分はOracleのバージョン等の環境により変化します。)
explain plan set statement_id = 'stateid1' forここでは表示結果の詳しい内容については説明しません。(詳しくは技術資料のPDFドキュメントを参照)
select * from tablename ←この部分に調べたいSQL文を記述する
/
select lpad(' ', 2*(level-1)) || operation || ' ' || options
|| ' ' || object_name || ' ' || DECODE(id, 0, 'Cost=' || position) "Query Plan"
from plan_table
start with id = 0
and statement_id = 'stateid1'
connect by prior id = parent_id
and statement_id = 'stateid1'
/
delete from plan_table where statement_id = 'stateid1'
/
commit
/
基本的には表示結果の上から順に評価されているようです。
表の絞り込みの順序は正しいか?インデックスを効果的に使用しているか?等をSQL文を変えながら確認してください。
※注 たとえ同一のSQL文であったとしてもANALYZE(統計情報)を取得すると実行計画の内容が変わる事があります。
久々の更新です。 PL/SQL中で一時停止したいときは以下のとおりです。dbms_lock.sleep(1); -- 1sスリープするちなみに1秒以下の値も設定できます。dbms_lock.sleep(0.1); -- 100msスリープする
dbms_lock.sleep(0.01); -- 10msスリープする
PL/SQL中で「DELETE FROM TblName;」とかって実行した時に何件削除したかは、SQL%ROWCOUNTに格納されます。 暗黙のカーソルには”SQL”っていう名前が付いていたんですね〜。 知りませんでした・・・ これを応用して以下の様に記述すると、大量件数削除する時にサーバーの負荷を上がりにくできます。loop DELETE FROM aaa WHERE bbb = 1 and ROWNUM < 100; dbms_lock.sleep(0.1); -- 100msスリープする EXIT WHEN SQL%ROWCOUNT=0; end loop;
utl_file_dirで複数のディレクトリを指定するときはカンマで区切るのですが、単にカンマで区切るとORA-20091(INVALIDPATHパスのエラー)が発生します。
複数指定したい時は、SPFILEに以下の様にディレクトリ名を”(シングルクオーテーション)でくくっって記述してください。
utl_file_dir='C:\aaa','C:\bbb'
クライアントのoracle\ora92\network\ADMIN\sqlnet.ora の
SQLNET.AUTHENTICATION_SERVICES= (NTS)という行を ↓
#SQLNET.AUTHENTICATION_SERVICES= (NTS)に変更します。 これでログイン認証時に無駄にかかっていた時間を短縮できます。
SQLNET.AUTHENTICATION_SERVICES= (NONE)
デバッグ時等に時間計測したりするときにms(ミリ秒)単位の現在時間を取得したい時は以下の様に記述します。
set serveroutput on
declare
ti TIMESTAMP;
begin
ti := SYSTIMESTAMP;
dbms_output.put_line(ti);
end;
/
実行結果は・・
04-03-23 09:41:12.460000日付書式はOracle 初期化パラメータのNLS_TIMESTAMP_FORMATで規定できます。(オラクルの再起動が必要となります)
utl_fileパッケージには、C言語に慣れていると間違いやすい幾つかの「クセ」があるようです。その@
utl_file.putを使用してファイル出力する時は、putした文字列が32Kbyteになる前に「utl_file.fflush」する必要がある。
32Kbyteを超えて出力後にファイルクローズしても、32Kbyteまでの分しか出力されない。
(今マニュアルを読んでみたらそう書いてありました・・・)
しかし、いちいちflushしないといけないなんて面倒です。(−−;そのA
2つのファイルをオープンし(1つはReadモード。もう1つはWriteモード)片方でget_lineしたものをもう片方にputするような時、改行文字(chr(10))を最後に付けないで複数行書き込むとエラー(WRITE_ERROR)になる。
get_lineした文字列には改行文字が含まれていると思われるのですが、putの時それを認識せず1行のバッファをオーバーしてしまう様です。
テキストBOX中で右ボタンクリックすると標準では"コピー”、”張り付け”等のメニューがでます。
これを自分で作成したメニューに変えるときはMouseDownイベント中に以下の様に記述します。Private Sub text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)また、アプリケーションキー(キーボードの右下のコントロールキーの左側に付いているキー)や、Shift+F10を押してメニューを表示する場合のためにKeyDownイベントに以下の様に記述します。
If Button = vbRightButton Then
text.Enabled = False
text.Enabled = True
text.SetFocus
PopupMenu mymenu, vbPopupMenuRightButton
End If
End Sub
(mymenuはメニューエディタで編集したメニューの名称)Private Sub text1_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = 93 Or (KeyCode = 121 And Shift = 1) Then
text1.Enabled = False
text1.Enabled = True
text1.SetFocus
PopupMenu mymenu, vbPopupMenuRightButton
End If
End Sub
Formのプロパティでは枠やタイトルバーの大きさ分からないですよね!
そんなときは、WINDOWS APIのGetSystemMetrics()関数を使用します。Public Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As LongConst SM_CYCAPTION = 4 ' タイトルバーの高さ取得
Const SM_CXFRAME = 32 'フォームのフレームの幅取得
Const SM_CYFRAME = 33 'フォームのフレームの高さ取得Global gCapHeight As Integer
Global gFrmWidth As Integer
Global gFrmHeight As Integer
Global gTwipX As Integer
Global gTwipY As IntegerPublic Sub GetSystem()
gTwipX = Screen.TwipsPerPixelX
gTwipY = Screen.TwipsPerPixelY
gCapHeight = GetSystemMetrics(SM_CYCAPTION) * gTwipY
gFrmWidth = GetSystemMetrics(SM_CXFRAME) * gTwipX
gFrmHeight = GetSystemMetrics(SM_CYFRAME) * gTwipY
End Sub
32ビット版のVBではString型の内部形式がUNICODEのためDLL呼び出し時にVBが勝手にUNICODE→ASCII変換を行います。
したがって、文字以外のバイナリーデーターを渡す時はSTRING型ではなくてByte型の配列を使います。
ところが・・・これがうまくいかなくて悩みました(^−^;
結論としては、VBでDLLの関数を宣言する時に、ByRefのAny型(Byte()のような配列ではない)で宣言し、関数を使用する時は'宣言部分
Public Declare Function DllFunc Lib "tekito.dll" (ByRef Msg As Any) As Long'呼び出し部分
DllFunc(A(0))
の様に、配列の先頭を指定する様にする必要があるようです。A(0)の(0)が重要です。
けっこう悩みました・・・
通常グリッド上で左ボタンを押すとカレントのセルがマウスの位置に移動します。
でも右ボタンを押したときは移動しません。
右ボタンを押したときにもカレントセルを移動したいときはMouseCol、MouseRowプロパティを使用します。
たとえば、GRDListという名前でMsFlexGridコントロールを定義している場合に行のみ右クリックした位置に移動したいとします。
その場合はMouseDownイベントに以下の様に記述します。
Private Sub GRDList_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbRightButton Then
If GRDList.MouseRow > 0 Then
GRDList.Row = GRDList.MouseRow
End If
End If
End Sub
”If GRDList.MouseRow > 0 Then”としているのは範囲外(−1)とタイトル行(0)を対象外にしているためです。
あるディレクトリに含まれるファイル名の一覧を取得する方法なんですが、VCでこれを実現するのに結構苦労しちゃいました。それと言うのも、昔やったことのある方法が全然使えなかったからなんです。(^_^;
昔にやったのは、カーニハン/リッチー著の「プログラミング言語C」という本にも載っているやり方で、ファイルをオープンするのと同じ関数でディレクトリをオープンしてファイルの中身を読むようにディレクトリリストを取得するというやり方でした。int fd;ところが、VC(ver.5)で上記のようにしてディレクトリーをオープンしようとすると-1が帰ってきて終わっちゃうんです!(知ってるひとから見れば当たり前?)if((fd = open(dirname, O_RDONLY, 0)) != -1) {
while(read(fd, (char*)dirbuf, sizeof(dirbuf)) == sizeof(dirbuf)) {
printf("%s\n", dirbuf);
}
close(fd);
}
で、オンラインマニュアルを調べて見ると・・・ありました。find系関数というものが。
この関数は指定したディレクトリ内のファイル(ワイルドカード指定もできる!)を取得できます。long hFile;のようにします。
struct _finddata_t c_file;/* ファイル一覧取得 */
if((hFile = _findfirst("c:\*.*", &c_file)) != -1L) {
printf("%s\n", c_file.name);while( _findnext(hFile, &c_file) == 0) {
printf("%s\n", c_file.name);
}
}
_findclose( hFile );
_finddate_t構造体にはアトリビュート情報とかいろいろ入っているみたいです。(詳しくはオンラインマニュアルを)
明日は早朝6:30から全館停電。メールとWebのサーバーは"shutdown -h 06:00"というコマンドで朝6時に自動的に落とすようコマンドを打って帰りました。
どうやら無事shutdownができたようですが・・・・・なぜかWebサーバーの方にTelnetでLoginできません。
メールサーバーに一旦Loginしてからリモートログインしようとすると
The system is going down on Fri Feb 16 06:00:51 2001というメッセージが表示されてしまいます。
Login incorrect
使用しているOSはメールサーバーの方がTurboLinux3.0でWebがTurboLinux4.0です。
なんでじゃ〜!!と調べて見ると・・・ありました。
/etc/nologinどうやらこのファイルがあると、管理者権限を持たないユーザーのLoginができなくなるらしいのです。
案の定、このファイルが残っていました。
メールサーバの方はこの現象が起きなかったので、shutdownの仕様が変わったのかバグでしょう(^−^;
このファイルを削除したらちゃんとLoginできるようになりました。