2023年1月3日火曜日

【Oracle】 ソート後ROWNUMを使用して指定位置のレコードを取得する

 やりたいこと

テーブルを検索しAカラムで昇順ソート後、ソート順が2番目となるレコードを取得したい。
2行目を指定となるとROWNUMあたり使って取得するSQLを考えてみる


検証作業
Oracle標準のDEPTテーブルで試す
DEPTテーブルを検索しDNAMEで昇順ソート後2行目となるレコードを取得出来るSQLを作成する。

①DEPTテーブルをROWNUM付けて取得
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT
※SELECT ROWNUM, * FROM DEPT → *にするとエラーになってしまった。

ROWNUM DEPTNO DNAME LOC
"1" "10" "ACCOUNTING" "NEW YORK"
"2" "20" "RESEARCH" "DALLAS"
"3" "30" "SALES" "CHICAGO"
"4" "40" "OPERATIONS" "BOSTON"

②この状態でROWNUMを指定し結果を確認する

SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT WHERE ROWNUM=2
あれ結果が返ってこない
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT WHERE ROWNUM<3
これだと2行帰ってくるな。
ROWNUM DEPTNO DNAME LOC
"1" "10" "ACCOUNTING" "NEW YORK"
"2" "20" "RESEARCH" "DALLAS"


③ ②のSQLにORDER BYを追加して取得
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT ORDER BY DNAME

ROWNUM DEPTNO DNAME LOC
"1" "10" "ACCOUNTING" "NEW YORK"
"4" "40" "OPERATIONS" "BOSTON"
"2" "20" "RESEARCH" "DALLAS"
"3" "30" "SALES" "CHICAGO"
ん~ORDER BYは結果に対して並べ替えなんでこれだとROWNUMは
元々のテーブルのROWNUMなんで指定が出来ない。

③ 検索結果の結果セットを副問合せとしROWNUMを確認する。
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM (
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT ORDER BY DNAME
)

ROWNUM DEPTNO DNAME LOC
"1" "10" "ACCOUNTING" "NEW YORK"
"2" "40" "OPERATIONS" "BOSTON"
"3" "20" "RESEARCH" "DALLAS"
"4" "30" "SALES" "CHICAGO"
ROWNUMが期待通りに変わったので取得できそうだけど②の状態なんで
ROWNUM=2って動作するのか?

④ ROWNUMを検索条件に指定してやってみる
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM
(
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT ORDER BY DNAME
)
WHERE ROWNUM=2
やっぱり無理かROWNUM<3として試す

ROWNUM DEPTNO DNAME LOC
"1" "10" "ACCOUNTING" "NEW YORK"
"2" "40" "OPERATIONS" "BOSTON"
ROWNUM=2って指定出来れば出来そうだが
Oracle内部のことはよくわからんがROWNUMの採番方法が関係しているのか?

⑤ さらに副問合せを用いてROWNUMを含めた形としてみる
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM
(
SELECT ROWNUM AS RNUM, DEPTNO, DNAME, LOC FROM
(
SELECT ROWNUM, DEPTNO, DNAME, LOC FROM DEPT ORDER BY DNAME
)
)
WHERE RNUM=2

出来た!
ROWNUM DEPTNO DNAME LOC
"1" "40" "OPERATIONS" "BOSTON"

一応ROWNUMを出力すると1になっている。


結論
検証を頼まれてやっては見たが。。
ROWNUMは結果に対して付番されるってことで条件に入れると結果が正しく帰ってこないって感じなのかなって
Oracleの仕様書読めば良いかもしれないが読む気になれず。

2015年5月29日金曜日

Variant型引数をもつDLLの呼出(VisualStudio2013)


今回の仕事も古いDLLを使用して相手装置のログを読み出す開発。

DLLの説明書にはVB6.0での使用方法しか書かれていない。
発注元からVisualStudio2013から呼び出せるのか確認してもらうと
実際に確認していないが動作するはずですとの回答。
なんとかなるよなと。。。


正直、私が使ったことがあるのVisualStudio6.0で以降の開発環境って今回が初めて。
ここ数年Javaがメインで開発しているしMicrosoft製品っていうかDLLとかよく判らない。
動的なライブラリってのはわかってますが。



OS     Windows7 Pro 32Bit版
開発環境 Visual Basic 2013

簡単にやりたいことを書き出すとアナログ値を収集する装置から
一定間隔で値を取得しDBへ保存する。
収集装置から値を取り出すのにDLLを使用する。


DLLをどうやって使うのかも微妙な感じで社内のMicrosoft製品での開発に詳しい人に聞いて何とかDLLをソースで呼び出せるようになる。

さて実行して見ると実機を前にDLL側から提供されているInit()、Open()は問題なく呼び出せた。
で、実際に値を読み取るメソッドでエラーとなってしまう。

このメソッドの引数が
Method(String , String, variant)

ソースはこんな感じ

Imports VS6Lib
class test(){
Private vs6Lib As VS6Lib.VS6COMM
sub test() {
Dim ret As Long
Dim ret As Object = new Object
vs6Lib = new VS6Lib.VS6COMM()
'' DLL初期化
ret = vs6Lib.init()
IF ret<>0 THEN
'' エラー処理
ENDIF
'' 装置へ接続
ret = vs6Lib.open()
IF ret<>0 THEN
'' エラー処理
ENDIF
'' アナログ値読み出し
ret = vs6Lib.Method("装置名","ALL_READ", obj)
IF ret<>0 THEN
'' エラー処理
ENDIF
~DB登録等の処理~
}
}

variant型はObject型に置き換えるとの記載を目にしたのでvariantの引数はObjectを
渡しているのだがDLLから帰ってくるエラー内容は、データ配列数が少ないとなっている。

このメソッドを使用するとvariant型に取得したいアナログ値をByte配列にして
戻してくれるらしいのだが…

同じ動作をするメソッドでvariantの箇所をStringでファイル名を指定するとファイルに出力してくれるメソッドがあるのでこちらを試してみると正常終了となりファイルも作成されてて当然値も出力されている。

やっぱりvariant型への引数の渡し方が悪いんだね。

調べてみても有用な情報は見つからず…
既に値が取れる前提で以降の処理は作成されているため変更は手戻りが大きすぎる。

なんとか引数を渡せないのか?
DLLからはByte配列が戻ってくるのでByte配列を引数に指定してみるが型が違うとエラーになる。当然か…

やっぱりファイル経由で値を取らないと駄目なのか?

しばし、考察


variant型の使い方を見ているとユーザ定義型などをvariant型引数に突っ込んだり出来るらしい。
もしかしてObject型にもユーザ定義型って突っ込めるのか?

そう思い試してみることに

'' Byte配列を定義
Dim b(0 To 64) As Byte
obj = b
'' アナログ値読み出し
ret = vs6Lib.method("装置名","ALL_READ", obj)
IF ret<>0 THEN
'' エラー処理
ENDIF

こんな感じで試してみるとエラーは発生しないで値を取れました!

取れたんですがさらに厄介なことに
 byte, byte,       byte, byte, byte, byte
 状態, ステータス, 値(Single 4byte)
ってな状態で返してくれているらしい。浮動小数点byteの中見ても判らんし正しい値が取れているのか?

Singleは浮動小数点なのでbyteを単純に変換出来るのか?
と調べてみると
System.BitConverter.ToSingle(byte配列, 開始位置)
Byte配列からシングルへの変換メソッドが有りました

        Dim sVal as Single = System.BitConverter.ToSingle(b, 2)

こんな便利なメソッドが用意されているなんて素敵です。
これで無事値を取得することが出来ました。


しかしVBって結構沢山の人が使っていると思うのだが情報を見つけるのが大変。
検索の仕方が悪いのか目的の情報になかなかたどり着けない。
情報が多すぎるんだろうなきっと。。。


この装置が出来てから10年近く立ってるんだったらDLL位更新して欲しいよな。後64bit版もね
ハード屋さんなんでソフトを再作成するの面倒なのかな?

2015年5月18日月曜日

外字登録後、Windowsで日本語が正常に表示されなくなってしまった時の対処法


仕事の都合で客先で使用している外字をフォントファイルに組み込んで使用していたのですが、仕事が終了したことも有り外字ファイルを削除したところ、プリンターの設定画面やAdobeReaderでPDFを開くとメニューの日本語が表示されなくなってしまいました。
外字自体はきちんと表示されていたんだが…

日本語部分が表示されない…

日本語部分が全く表示されず、操作が出来ない状態。
せめて英語表示の方がまだ判るんだが。


ネットを調べても同様の現象は見つからない。。。
文字化けの対処方法はあっても日本語が表示されないってのはあまり起きないんだろうな。

たどり着いたのがレジストリ値。
regeditを使用して外字フォントを修正します。


スタート
 プログラムとファイルの検索欄にregeditと入力

  以下のレジストリを編集
  コンピューター
   |-HKEY_CURRENT_USER
    |-EUDC
     |-932
      +SystemDefaultEUDCFont

ここの値が組み込んだ外字ファイル名となっていたので936、949と同じEUDC.TTEに変更。
変更後、再起動を行ったら無事復旧しました。
※regeditで他の値を修正するとwindowsの挙動がおかしくなるので注意!

ダブルクリックすると編集できます

OKで登録されます

この外字ファイルを組み込んだ際に使用したツールで削除したつもりだったんだが上手く解除できていなかったのか?

外字ファイルを変更したらeudc.tteに戻す必要があったんだね。

これなら操作出来ます。



使用しているPCはWindows7 Professional 64bit版です。

同じ現象で苦しんでいる人の助けに慣れれば幸いです。



2014年12月29日月曜日

Javaからのストアド呼出(TYPE RECORD)方法

ハマりました。
と、言うよりJava(JDK)とOracleを知らなすぎだっただけかも知れません。
JavaからOracleのストアドを呼び出すのは簡単なのですがストアド側の引数でTYPE RECORD型に対して引数として渡せないんです。

ネットの検索の仕方が悪いのか全く情報が見つからない。
同じようなことで苦労している人は居ないのだろうか?それとも常識すぎるのか?
今回自分がJabaからOracleのストアドを呼出するの初めてでOracleの知識も少ないせいかな?

ストアドはこんな感じです。(あくまでも参考ですので…)

/* レコード型定義 */
type TYPE_TEST_RECORD is record(
 numFIELD1 TEST.FIELD1%type
, numFIELD2 TEST.FIELD2%type,
 numFIELD3 TEST.FIELD3%type
);
/* PL/SQL表定義 */
type TYPE_TEST is table of TYPE_TEST_RECORD index by binary_integer;
/* ストアドI/F */
PROCEDURE ProcTEST (
 numRETURN_STS OUT NUMBER, /* リターンステータス */
 numPROCESS_CD IN NUMBER, /* 処理区分 */
 usrTYPE_io IN OUT TYPE_TEST /* データ格納領域 */
);

これをJavaのJDKのthinドライバから呼び出すことが出来ないんです。
TYPE_TESTへ値を渡せない。
Object型等色々試しても一向に拉致が空きませんでした。


そのため、いろいろ調べてみるとJPublisherなるものを使用すれば動作させることが出来るとOracleのページに記載があり調べてみたのだがよくわからん。
JDBCのOCIドライバにすれば出来るんじゃないの?なんて記載も見つけたが…
同じ箇所にストアドの中でTYPE RECORD型に値を設定しなおせば大丈夫との記載も。あまり推奨されていないようだが。

ストアド経由で更新処理だけを行うので簡単に済ませたい。
試しに以下のようにしてストアドの呼出関数を1枚かぶせて見たところ上手く行った。

/* ストアドI/F(Java用) */
PROCEDURE ProcTESTJ (
 numRETURN_STS OUT NUMBER, /* リターンステータス */
 numPROCESS_CD IN NUMBER, /* 処理区分 */
 numFIELD1 IN NUMBER,
 numFIELD2 IN NUMBER,
 numFIELD3 IN NUMBER );
PROCEDURE ProcTESTJ (
 numRETURN_STS OUT NUMBER, /* リターンステータス */
 numPROCESS_CD IN NUMBER, /* 処理区分 */
 numFIELD1 IN NUMBER,
 numFIELD2 IN NUMBER,
 numFIELD3 IN NUMBER
)
 IS
 test_rec TYPE_TEST ;
BEGIN
 test_rec(1).numFIELD1 = numFIELD1 ;
 test_rec(1).numFIELD2 = numFIELD2 ;
 test_rec(1).numFIELD3 = numFIELD3 ;
 ProcTEST (
  numRETURN_STS, /* リターンステータス */
  numPROCESS_CD, /* 処理区分 */
  test_rec /* データ格納領域 */ );
 END ProcTESTJ;

Javaでの呼出はTYPE RECORD型のメンバ数が増えると面倒だがこれで何とか出来る。

Java側はこんな感じです。
第一引数にリターン値が帰ってきます。

String sql="{call ProcTEST(?, ?, ?, ?, ?) }" ;
connection = DriverManager.getConnection(url, userid, password);
OracleCallableStatement callableStatement =    
                       (OracleCallableStatement)connection.prepareCall(sql); callableStatement.registerOutParameter( 1, java.sql.Types.INTEGER) ;
callableStatement.setInt( 2, 1) ;
callableStatement.setInt( 3, field1) ;
callableStatement.setInt( 4, field2) ;
callableStatement.setInt( 5, field3) ;
callableStatement.executeUpdate() ;
returnSts = callableStatement.getInt(1);

もっと良い方法もあると思いますが、JavaからストアドのTYPE RECORD型へ値を渡す際の参考になればと思います。
特にリプレース案件などでストアド使っているシステムをJavaに置き換えるなんて時に同じ現象にぶち当たる人いそうなんだけけどね。

2014年10月12日日曜日

HTML:画像の上にポップアップ表示させたい場合


今月末納品するリプレース案件のシステム。

一部コンテンツを一般公開するわけなんだけどHTMLに不備が見つかってしまう。

地図画面の上にこんな感じでポップアップ表示をさせていたんだけどIE8,IE9では表示されるのだがFireFox,Chrome,IE10,IE11では表示されない…。
赤丸部分にカーソル合わせてください。これが出なかったんですよIE11だけは




HTMLはこんな感じ
<IMG src="test.png" useMap="#POPUP">
<MAP name="POPUP" alt="test">
<!-- ポップアップタグ -->
<AREA href="sample.html" shape=circ alt="ここは〇〇&#13現在:××&#13;1時間後:▲▼" coords=234,156,20></AREA>
</MAP>


調べた結果IE11以外は<AREA alt="~" の部分を<AREA title="~" に変更することで無事解決

IE11だけはどうしても表示されない。
<AREA タグ内でonMouseOver,onMouseOutでDIVの表示に切り替えないと駄目かな?なんて考え始めたのだが確かJavaScriptは極力使わない方針だったので躊躇してしまう。

しかしなんでIE11だけ駄目なんじゃ!?

表示出来ないt多分納品出来ないのでJavaScriptで表示するのも視野に入れて作成しているとあれ!?なんか出るようになったぞ!

そういえば今IMGタグの終了タグ入れたよな!?

まっまっまさか!
</IMG>

この終了タグが抜けていたせい!?

なんでIE11だけ駄目なんだよ。全部駄目だったらもっと早く気がついたのに…
このコンテンツって何も変えていなくてIE11 での表示確認だけだったのに酷い。。。

かなり無駄な時間を使ってしまったではないか…

終了タグはちゃんと入れましょうね。

終了タグは全く関係ありませんでした。
無くても問題なく表示されました。

表示がポップアップの表示が遅いだけだったかも知れません。。。





2014年7月16日水曜日

リモートデスクトップでコンソールセッションに接続


Windows2003Serverへリモートデスクトップでアクセスし起動中のアプリを停止させようとしたら動いているはずのアプリがいない!
タスクマネージャのプロセスではちゃんと動いているのに~なんて事無いですか?


これはリモートデスクトップで接続した時に違うセッションに接続しているためだそうです。
その場合、コマンドプロンプトから以下のコマンドで接続するとコンソール・セッションに接続できます。

c:\>mstsc.exe /console /V:xxx.xxx.xxx.xxx


/Vは接続先のIPアドレスを入れます。
IPアドレスをしてしないとリモートデスクトップ接続画面で接続先を指定する事になります。

接続するときは毎回コンソールセッションに繋ぎたい場合などは上記の一行をメモ帳などに書き込んでmstsc-aaa.batなんてバッチファイルにしておけば便利ですね。



Windows7にてhostsファイルを編集するには


hostsファイルってご存じですか?

ネットワークのIPアドレスとホスト名(マシン名)を紐付けてくれるファイルです。
最近は使うことがあまりないと思うのですが古いシステムなどでは使用していたりするんです。

昔のシステムってアプリケーションとかに直接IPアドレスとか持っていたりするのでこれをホスト名にしておけばhostsファイルの修正だけで済むわけです。

が、今となってはこのファイルを簡単に変更できてしまうと悪意のあるサイトに簡単に誘導できてしまうためセキュリティが強化され編集出来ないんですよね。

ちょっと古いシステムを動かしたいのでhostsファイルを編集したいんですが…

秀丸エディタで開くとアクセス制限がかかっていて開くことは出来ても保存が出来ません。
プロパティとか見ても権限変更出来ないし…
まだ、Windows7を使い始めて半月なので正直どうすりゃいいんじゃ?って感じ



ネットでいろいろ調べて見ると管理者権限で実行すればよいなんて見つかる。
ふむふむ、秀丸エディタのアイコンを右クリックして「管理者として実行」すると編集することが出来ました!こんなメニューがあるなんて知らなかったよ。
※メモ帳でも同じように管理者で実行すれば問題なく編集できるはずです。


注意!
hostsファイルをドラッグ&ドロップで秀丸、メモ帳に渡しても表示すらしないです。
エディタのメニューでファイルを開くから開かないと編集出来ません。
(一体どんな理由なんだろうか?)


今回はhostsファイルを例にしましたが「ファイルが編集できない~!」なんて時はメモ帳やエディタを「管理者として実行」してみましょう!