バルクバインド(BULK BIND)の例

セレクト結果を一行づつ処理したいってとき、BULK BIND(BULK FETCHもあるみたい)っていうこともoracleではできるんですね。

-- バルクバインドの例
SET SERVEROUTPUT ON

DECLARE
  TYPE TLINE IS TABLE OF テーブル名%ROWTYPE INDEX BY BINARY_INTEGER;
  vLINE TLINE;
BEGIN
  SELECT * BULK COLLECT INTO vLINE FROM テーブル名;
  FOR i IN 1 .. vLINE.COUNT
  LOOP
    DBMS_OUTPUT.PUT_LINE('No=' || vLINE(i).カラム名);
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('取得行=' || vLINE.COUNT);
END;
/

参照は以下。

連載 PL/SQLを使ってみよう!第61回「バルク・バインド(SELECT BULK COLLECT INTOの場合)」 | 集合研修 システム・テクノロジー・アイ銀座会場

ちなみに、BULK FETCHは以下。

バルクフェッチ - オラクル・Oracle PL/SQL 入門

SQLで2つのカラムに分割された最後のCR、最初のLFを調べる方法

oracleで2つのカラムにCRLFが分割されて保存されてしまった場合を調べる方法。

select id from テーブル
  where desc1 like '%' || chr(13) and desc2 like chr(10) || '%';

ちなみに以下の正規表現ではできなかった。

select id from テーブル
  where regexp_like(desc1, '\r$');

レコード確認してからのINSERT

レコードがない場合に限りinsertする方法。キーにしてエラーにすればよいって言われるのはわかっているけど、エラーメッセージ見たくない(見せたくない)人向け。

INSERT INTO テーブル(col1, col2)
  SELECT '値1', '値2' FROM DUAL
    WHERE NOT EXISTS(SELECT 'X' FROM テーブル WHERE col1 = '確認したい値');

テーブルの検索結果をNOT EXISTSで判定して、INSERT SELECTでインサートする。

参考サイトは以下。

http://scrap.php.xdomain.jp/exists_insert/

Oracle NVARCHAR2(1600)で1600文字入らない件

NVARCHAR2のカラムを大きくして大量文字を入力しようとすると以下のエラーが発生して入力できない。

エラー:: ORA-01461 : LONG値はLONG列にのみバインドできます。

ググってみたところ、

http://www.projectgroup.info/tips/Oracle/errorcode/ORA01461.html

のサイトが出てきた。

Oracle 11G R2 Client (11.2.0.1) のODBCドライバーの不具合

とのことでバージョンを確認したところ、こちらの環境はOracle Clientは11.2.0.4でした。残念。で、エラーメッセージが崩壊しているのは置いておいて、どこまで入るのだろうと根気強く入力値を変化した結果、1333文字までしか入らないことがわかった。

NLS_LANG=JAPANESE_JAPAN.JA16SJISTILDE

と指定しているのでShift_JIS(2byte)なんだけど、1333×3=3999つまり4000byte境界があるっぽい(内部で3byteで計算してるのか?)。
なんだかとても納得行かない仕様。

ちなみに確認したOracleサーバ側のバージョンは11.2。

Oracle NVARCHAR2を文字列連結するときの注意

検索したい該当カラムがNVARCHAR2でも連結すると暗黙変換でVARCHAR2となるらしく、4000byte以上はエラーとあるため、以下のようにTO_CLOB('')を連結しとく必要がある。

SQL> select col1 from foo_table where (TO_CLOB('') || col1 || col2) like '%検索%';

確認したOracleのバージョンは11.2

MacOS Xのはがきデザインキットが起動しない件

日本郵政の年賀状ソフトウェア「はがきデザインキット」を2015年の年末に使っていたのに2016年になってからなぜか使えなくなっていた。
ダブルクリックしてもソフトウェアが起動しない。コマンドラインから起動してみたら以下のようになる。

$ cd /Applications/
$ open -a はがきデザインキット
LSOpenURLsWithRole() failed for the application /Applications/はがきデザインキット.app with error -10810.
$

これだとわからないのでコンソール.appを起動して見てみた。

2016/01/17 10:00:11.423 はがきデザインキット[2427]: renaming failed with this error: Error Domain=NSCocoaErrorDomain Code=513 "“MacOS”へのアクセス権がないため、“はがきデザインキット”を移動できませんでした。" UserInfo=0x21a800 {NSSourceFilePathErrorKey=/Applications/はがきデザインキット.app/Contents/MacOS/はがきデザインキット, NSUserStringVariant=(
Move
), NSFilePath=/Applications/はがきデザインキット.app/Contents/MacOS/はがきデザインキット, NSDestinationFilePath=/Applications/はがきデザインキット.app/Contents/MacOS/はがきデザインキット_32, NSUnderlyingError=0x218b60 "操作を完了できませんでした。
アクセス権がありません"}

なんかパーミッションがないみたいなので以下を実行

$ cd はがきデザインキット.app/
$ cd Contents/
$ ls -l
total 16
-rwxr-xr-x@ 1 root  wheel  1926 11 29 14:44 Info.plist
drwxr-xr-x@ 3 root  wheel   102 11 29 14:44 MacOS
-rwxr-xr-x@ 1 root  wheel     8 10 28  2014 PkgInfo
drwxr-xr-x  8 root  wheel   272 11 29 14:44 Resources
$ ls -alF MacOS
total 72
drwxr-xr-x@ 3 root  wheel    102 11 29 14:44 ./
drwxr-xr-x@ 6 root  wheel    204 11 29 14:44 ../
-rwxr-xr-x@ 1 root  wheel  34604 10 28  2014 はがきデザインキット*
$ sudo chmod 777 MacOS
Password:
$

この後、ダブルクリックしてみたところ起動成功。OSバージョンアップしたわけでもソフトウェアをバージョンアップしたわけでもないのでこうなってしまった原因は不明。

で、起動成功後、バージョンアップを促される。なのでバージョンアップした。
バージョンアップした後、変更したディレクトリパーミッションだとまずいので見てみたところ、

$ pwd
/Applications/はがきデザインキット.app/Contents
$ ls -alF
total 16
drwxr-xr-x@ 6 root  wheel   204  1 17 10:06 ./
drwxr-xr-x  3 root  wheel   102  1 17 10:06 ../
-rwxr-xr-x@ 1 root  wheel  1926  1 17 10:06 Info.plist*
drwxr-xr-x@ 3 root  wheel   102  1 17 10:06 MacOS/
-rwxr-xr-x@ 1 root  wheel     8 11 22 16:08 PkgInfo*
drwxr-xr-x  8 root  wheel   272  1 17 10:06 Resources/
$ ls -alF MacOS
total 64
drwxr-xr-x@ 3 root  wheel    102  1 17 10:06 ./
drwxr-xr-x@ 6 root  wheel    204  1 17 10:06 ../
-rwxr-xr-x@ 1 root  wheel  31252 11 22 16:08 はがきデザインキット*
$

ディレクトリのパーミッションは元どおりになっている。これで起動できないのかと思っていたけど普通に起動できる。
エラーメッセージでは"_32"(32bitかよ)にリネームしようとしてパーミッションがないとか言っているんで、実はまずいバージョンを掴まされてただけ?

Javaスレッドで異なるインスタンスでも同期するためには

Javaでスレッドで同期するにはsynchronizedを使用するが、

synchronized public void foo() {
	....
}

public void foo() {
	synchronized (this) {
		....
	}
}

とした場合、同じインスタンスでの同期となる。
スレッドが異なるインスタンスを使用して同期しなきゃいけない時は、同期用オブジェクトをsynchronizedに指定してやれば良い。例ではクラス変数を参照して同期している。

package com.foobar.console;

/**
 * スレッド同志で異なるインスタンスでも同期できるかのテスト
 */
public class Test00 {
	private static final int MAX_THREAD = 500;
	public static int counter = 0;
	public static final Object LOCK = new Object();	// 同期用オブジェクト
	
	public static void main(String[] args) {
		// スレッド生成
		MyThread[] threads0 = new MyThread[MAX_THREAD];
		MyThread[] threads1 = new MyThread[MAX_THREAD];
		for (int i = 0; i < MAX_THREAD; i++) {
			threads0[i] = new MyThread(new Counter());
			threads1[i] = new MyThread(new Counter());
			threads0[i].start();
			threads1[i].start();
		}
		// スレッドが全て終了するのを待つ
		for (int i = 0; i < MAX_THREAD; i++) {
			try {
				threads0[i].join();
				threads1[i].join();
			} catch (InterruptedException e) {
				System.out.println(e);
			}
		}
		// カウンタを表示
		System.out.println(counter);
	}
}

/**
 * スレッドクラス
 */
class MyThread extends Thread {
	private Counter counter = null;

	public MyThread(Counter counter) {
		this.counter = counter;
	}

	public void run() {
		this.counter.countUp();
	}
}

/**
 * カウンタクラス
 */
class Counter {
	public void countUp() {
		synchronized (Test00.LOCK) {
			System.out.print(".");
			int n = Test00.counter;	// カウンタ値を読み込み
			System.out.print("o");
			Test00.counter = n + 1;	// 加算してカウンタに設定
			System.out.print("O");
		}
	}
}

参考にしたのは以下のサイト。

http://www.tohoho-web.com/java/thread.htm#synchronized