プログラマメモ2 - programmer no memo2

Coffmanの循環待機条件 2025/11/03

お仕事で、書き込みの順序性を保つのにコードマスターにロックキーを用意して行ロック(select for update)で実現している方式に出会いました。
なぜにコードマスターで、思わないこともないのですが、そういうものなのです。
ロックキーは1テーブルごとへの書き込みを表現しているようで、複数のテーブルにまたがる場合には、どうするのですか。

A,B,C,Dというテーブルがあって、ある処理では、A,Bのみ、別の処理では、A,B,C,Dという具合。
直感で、やばい香りがするわけで、やばい匂いといってよいでしょう。
もともと、no waitをつけていない。よいこのみんなは真似してはいけないよ、のデッドロック(Dead Lock)。
こういう場合は、SQLを処理ごとにまかせるのではなく共通処理にして呼び出すというのは、多くの人が気がつくことなのでしょう。
それでロックする順序が大事なわけです。
1行ロックをとる場合は、よいのですが、複数行を1セッション(プロセスといっていいのか)でロックする場合は、ロック行の順序が大事だよ。

SELECT * FROM AAAA WHERE a IN('A','B','C','D') ORDER BY a FOR UPDATE
ORDER BY大事よ。
理論的な背景は僕にはないのですが、


以下、クロードさんに尋ねました。
Coffmanの4条件(デッドロック発生の必要十分条件)※必要十分条件

必要条件 (Necessary Condition)
十分条件 (Sufficient Condition)
必要十分条件 (Necessary and Sufficient Condition)

Mutual Exclusion (相互排除)
Hold and Wait (保持と待機)
No Preemption (非横取り/非プリエンプション)
Circular Wait (循環待機)


以下、クロードさんに考えてもらったブルグのタイトルと内容
Coffmanの4条件、実務で崩せるのは実質1つ説」
実務的には:

相互排除: 崩せない(ロックは必要)
保持と待機: 崩しにくい(トランザクション分割は難しい)
非横取り: 崩せない(DBMS仕様)
循環待機: ORDER BYで崩せる! ← ここがポイント

えくせるでてすとでーたをつくるその1 2014/02/22

プロジェクトごとというか、会社さんというか、開発時のテストデータの作り方はいろいろありますね。

プログラムの仕様や、組んだSQL(これから組むであろう)の特性などから、テストケースおこして、それにあわせて、テストデータをつくるというのが一般的な流れかなとは思います。


エクセルで組んで出力するところもあれば、直接SQLをテキストファイルのSQLを編集しているところや、あとは、XMLや、CSV形式や、規模ややりやすさや修練度や、いろいろな兼ね合いがあります。

組あわせ数が多い場合、テストデータを検討する際にはやはりエクセル便利だなと。

それで、
「表」で表現できるのがエクセルの強みですが、テストデータを組むさいに、縦でもたせる場合をみたことなくて、たいてい、横に並べるパターンが多いかなというのが感想です。

個人的には、たいていのテーブル定義書が、縦で表現されているところから、縦に書いていくのが好きです(縦といっているのは項目のこと)。左に項目名を用意して、列がふえていく感じです。

ただし、この記述方法ですと、古いエクセルは列数の制限から、テストデータを多く作るのには向いてなくて、やはり、項目は縦にもたせるよりは、横なのかなー。

エクセルのマクロが嫌い(苦手)なのですが、なんか最後にはエクセルなんですよね。

というわけで、こんなの考えてみました。


作成するにあたって、はじめて、 TRANSPOSEを知りました。

とりあえず、downloadできる場所に

Index of /download/excel

いくつかのユーザー関数を定義しています。


' ******************************** ' 改変自由 2014/02/22 ' deiji.jp ' ******************************** ' ******************************** ' コンキャッツ ' いい名前がうかばなかったので ' ******************************** Function CONCATS(rng As Range, Optional a As Variant = ",") As String Dim cnt As Integer Dim ret As String Dim m As Integer Dim i As Integer i = 0 m = rng.Count ' Debug.Print m For Each r In rng i = i + 1 ret = ret & r.Value If i < m Then ret = ret & a End If Next CONCATS = ret End Function ' ******************************** ' 参照、クォート、コンキャッツ ' いい名前がうかばなかったので ' ******************************** Function REF_QT_CONCATS(ref As String, rng As Range, Optional a As Variant = ",") As String Dim ret As String Dim m As Integer Dim i As Integer i = 0 m = rng.Count ' Debug.Print m ' 同行にある、値をチェックして囲むか決定する For Each r In rng i = i + 1 ' Debug.Print ">>>" & ref & r.Row 'シングルクォートするかはSQTで判断する ret = ret & SQT(Range(ref & r.Row).Value, r.Value) If i < m Then ret = ret & a End If Next REF_QT_CONCATS = ret End Function 'シングルクォート Function SQT(a As String, b As String) As String ' "○"なら囲む If a = "○" Then SQT = "'" + b + "'" Else SQT = b End If End Function



sql IN句の数制限 2013/07/20
2013/07/22

oracleのIN句の中に記述できる要素は1000個ぽいですね。 で、怠惰の僕は1000を越える場合どうすればよいかと考えたわけです。 単純に1000個限界まで書いて、もうひとつIN句を用意してその続きを書いて、ORでつなげればいいんじゃないかと。

select * from xxxx where a IN(1000まで) or a IN(1001から..)
もうこれでいいんじゃないかと。 なんか数年前もこの件話題にした気する。。。 あとunionでつなげるよりは、わかりやすいと思ったりどうだろう。

追記
こういう記事あった。
S2Dao の IN 句で 1000 件以上のリストを渡すにはどうするか - 倖せの迷う森

白状しますと、昇順と降順の意味をおぼえていません。 2013/01/18

はい、白状しますと、昇順と降順の意味をおぼえていません。
じゃ、どうやってSQLのソート順を決定するのさ、といわれますと、SQLを書いて実行した結果で、決めています。と、いうとどん引きされそうですね。。。

お仕事では、ディスプレイの横に、意味とデータを書いて貼ったりして、当座をしのいでたりします。

同じ悩みの人いないかなーと思い検索しますと、いました。

昇順と降順が覚えられません。何か良い覚え方はありますか? - Yahoo!知恵袋

僕の意見では、これは記憶力というよりも、イメージと言葉が、その人の世界を認知する方法にそぐわないからだろうと思います。

上記のリンクの回答では、階段をイメージさせています。いい方法だと思います。

昇る(1,2,3...)
降りる(3,2,1...)

ただ、これを想起する方法を失敗すると、僕のように永遠におぼえきれない、もしくは思い出すのに時間がかかることでしょう。

ここんとこ、自分の仕事が遅くて時間がかかる理由を探っています。
 またひとつ理由がわかった気がします。


で、きっと僕は、毎回、ソートされるデータのイメージを自分自身で納得するためにSQLを書いて実行するでしょう。

EZSPLIT 2012/11/17

SQLです。
mysqlのファンクションです。
カンマ区切りの文字列中の何番目の文字列を取り出す目的で作成しました。
3〜4週間前に作成したと思うのですが、いまいちその動機を思い出せないでいます。
やばいですね....
関数名としてEZSPLITというのもいまいちだなーと思いつつ

DROP FUNCTION EZSPLIT;
delimiter //
CREATE FUNCTION EZSPLIT(s TEXT, idx INT) RETURNS TEXT
NO SQL
BEGIN
     RETURN SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(s, ','), ',' , idx),
',' , -1);
END
//
 使い方は
こんな感じ
mysql> select EZSPLIT('a,b,c',2);
+--------------------+
| EZSPLIT('a,b,c',2) |
+--------------------+
| b                  |
+--------------------+
1 row in set (0.00 sec)

何番目かの指定は1スタートとなっています。


LEFT -RIGHT - 外部結合で集合演算だそうです。 2012/11/17

SQLです。
やはりSQLは苦手です。ジョインとかインナージョインとかアウタージョインとかいうフレーズが聞こえると身構えてしまいます。...

差集合を求めるSQLです。
外部結合を使って差をもとめます。
LEFT側が大きい集合を想定しています。
LEFT - RIGHT = その差
というイメージしています。

用意するテーブルは以下
drop table a;
create table a (
  id          smallint unsigned not null auto_increment primary key
)
;
drop table b;
create table b (
  id          smallint unsigned not null auto_increment primary key
)
;

まず、aテーブルの状態

+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  6 |
+----+
つぎにbテーブルの状態

+----+
| id |
+----+
|  1 |
|  3 |
|  5 |
|  6 |
|  7 |
|  8 |
+----+

外部結合してみます。
aテーブルを左におきます。
こんな感じのSQLにしてみました。

SELECT l.id, r.id
FROM
( -- LEFT
SELECT * FROM a
) l
LEFT OUTER JOIN
( -- RIGHT
SELECT * FROM b
) r
ON l.id = r.id
;
 結果は、

+----+------+
| id | id   |
+----+------+
|  1 |    1 |
|  2 | NULL |
|  3 |    3 |
|  4 | NULL |
|  5 |    5 |
|  6 |    6 |
+----+------+
ここから本題
右側にないものはNULLとなる性質を利用して、NULLであるものだけを残せば、結果として、差がとれるということのようですね。
使うSQLは以下

SELECT l.id, r.id
FROM
( -- LEFT
SELECT * FROM a
) l
LEFT OUTER JOIN
( -- RIGHT
SELECT * FROM b
) r
ON l.id = r.id
WHERE
r.id IS NULL
;
結果はこんな感じ

+----+------+
| id | id   |
+----+------+
|  2 | NULL |
|  4 | NULL |
+----+------+

mysqlでストアドプロシージャ その6 - プロシージャ内から別のプロシージャを呼ぶ 2012/09/24

mysqlでストアドプロシージャ その6 です。

プロシージャ内から別のプロシージャを呼んでみます。

ついでにトランザクションに参加してるのか調べてみました。

参考


結論としてトランザクションに含まれるようですね。

DROP PROCEDURE IF EXISTS p000;
DROP PROCEDURE IF EXISTS p007;
delimiter //
-- sub procedure
CREATE PROCEDURE p000()
BEGIN
    INSERT INTO a VALUES(0, CURRENT_TIMESTAMP());
END;
-- main procedure
CREATE PROCEDURE p007()
BEGIN
    -- 以下の実験コードは、データベースエンジンがInnoDBとき確認できます。
    -- MyISAMだとロールバックされない。
  
    -- エラーの場合 ロールバック設定
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        SELECT "*** ROLLBACK!!";
        ROLLBACK;
    END;
  
    -- トランザクション開始
    START TRANSACTION;
  
    -- 呼び出されたprocedureもトランザクションに含まれるようだ
    CALL p000();
  
    -- PKの重複がおこるので、かならず失敗するのでロールバックされるはず
    INSERT INTO a SET id = last_insert_id();
    -- お決まりのコミット!!
    COMMIT;
END
//
CALL p007();-- ここで実行
DROP PROCEDURE IF EXISTS p007;
DROP PROCEDURE IF EXISTS p000;

mysqlでストアドプロシージャ その5 - ラベルを使ってcontinue,breakっぽく 2012/09/24

mysqlでストアドプロシージャ その5です。

ループでラベル使うとcontinue,breakっぽく使えますねと。

なんとなく下記のような感じで
DROP PROCEDURE IF EXISTS p004;
delimiter //
CREATE PROCEDURE p004()
BEGIN
  
    DECLARE done INT DEFAULT 0;
    DECLARE a_id INT;
    DECLARE cur CURSOR FOR
        SELECT id FROM a;
    -- この宣言で読み取り行がないことを検知します。
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
  
    OPEN cur; -- カーソルオープン
  
    main:REPEAT -- 繰り返し
        FETCH cur INTO a_id;
        IF NOT done THEN
         
           --  continueっぽく使える
           IF a_id < 20 THEN ITERATE main; END IF;
          
           -- 通常inset にwhereが使えなかったのでdualを使ってるけどこの辺自信がない...
            INSERT INTO b (ida) SELECT a_id FROM dual WHERE NOT EXISTS(SELECT 1 FROM b WHERE ida=a_id);
          
            -- breakっぽく使える
            IF a_id = 22 THEN LEAVE main; END IF;
      
        END IF;
    UNTIL done END REPEAT main;
  
    -- クローズ!!
    CLOSE cur;

END
//
CALL p004();-- ここで実行
DROP PROCEDURE IF EXISTS p004;

mysqlでストアドプロシージャ その4 - トランザクション 2012/09/23

 mysqlでストアドプロシージャ その4 です。
トランザクションの実験です。

どうもDBのエンジンによってトランザクションが有効無効がある感じですね。

参考


というわけで、データベースエンジンを切り替えて確認してみました。

どんな実験したかというと、INSERT文を二回流して、2回目のINSERTは1回目にINSERTとしたPKを使ってかならず失敗させます。

トランザクションが効けば、1回目のINSERTも登録されないはずというわけです。

たしかにMyISAMで、はトランザクションが効かず、InnoDBでは効きました。


テーブルは以下
 create table a (
  id          smallint unsigned not null auto_increment primary key,
  tday      timestamp
);
エンジンの変更は、以下のSQLで行います。
ALTER TABLE a engine = InnoDB;
ALTER TABLE a engine = MyISAM;

以下ストアド

DROP PROCEDURE IF EXISTS p006;
delimiter //
CREATE PROCEDURE p006()
BEGIN
    DECLARE x INT;
    -- 以下の実験コードは、データベースエンジンがInnoDBとき確認できます。
    -- MyISAMだとロールバックされない。
   
    -- エラーの場合 ロールバック設定
    DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
    -- トランザクション開始
    START TRANSACTION;
   
    INSERT INTO a VALUES(0, CURRENT_TIMESTAMP());
   
    -- PKの重複がおこるので、かならず失敗するのでロールバックされるはず
    INSERT INTO a SET id = last_insert_id();
    -- お決まりのコミット!!
    COMMIT;
END
//
CALL p006();-- ここで実行
DROP PROCEDURE IF EXISTS p006;

mysqlでストアドプロシージャ その3 - ROW_COUNT() 2012/09/22

mysqlでストアドプロシージャ その3です。
 ROW_COUNT()を使うのが今回の趣旨です。
ROW_COUNTは

先行するステートメントによって更新、インサート、または削除された行の数を戻します。

ってことらしい
参考
[MySQL]複数行を追加するためのinsert文Add Star
ROW_COUNT()


DROP PROCEDURE IF EXISTS p002;
delimiter //
CREATE PROCEDURE p002()
BEGIN
    -- 取得件数格納用
    DECLARE c INT;
  
    -- 1行で複数行追加する
    INSERT INTO a VALUES(0, CURRENT_TIMESTAMP()),(0, CURRENT_TIMESTAMP()),(0, CURRENT_TIMESTAMP());
    -- 直前の更新件数を取得
    SELECT ROW_COUNT() INTO c;
    -- 値を表示する
    SELECT CONCAT('rowcount:', c);
END
//
CALL p002();-- ここで実行
DROP PROCEDURE IF EXISTS p002;

mysqlの文字列結合のやりかたがわからない...
oracleだと||だったと思うんだけど...mysqlは....
row_countの結果で登録できたかどうかの判定できるのかな
下記のようなSQLで実験 判定できるみたい
DROP PROCEDURE IF EXISTS p003;
delimiter //
CREATE PROCEDURE p003()
BEGIN
    -- 取得件数格納用
    DECLARE c INT;
  
    -- 追加しないINSERT文
    INSERT INTO a(tday) SELECT  CURRENT_TIMESTAMP() FROM dual WHERE FALSE;
    -- 直前の更新件数を取得
    SELECT ROW_COUNT() INTO c;
    IF c = 0 THEN
        SELECT 'result:0';
    ELSE
        SELECT 'result:1';
    END IF;
  
END
//
CALL p003();-- ここで実行
DROP PROCEDURE IF EXISTS p003;

mysqlでストアドプロシージャ その2 2012/09/22

mysqlでストアドです。
oracleほどではないですが、使えて便利そうな予感です。

参考


ストアドプロシージャで実験

簡単なシナリオ

aテーブルに入っている値をbテーブルに挿入します。
SQL一本でできそうですが...

 以下テーブル

create table a (
  id          smallint unsigned not null auto_increment primary key,
  tday      timestamp
);

create table b (
  id          smallint unsigned not null auto_increment primary key,
  ida smallint,
  tday      timestamp
);

用意したプロシージャは以下
カーソル使ってます。
DROP PROCEDURE IF EXISTS p001;
delimiter //
CREATE PROCEDURE p001()
BEGIN
  
    DECLARE done INT DEFAULT 0;
    DECLARE a_id INT;
    DECLARE cur CURSOR FOR
        SELECT id FROM a;
    -- この宣言で読み取り行がないことを検知します。
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
  
    OPEN cur; -- カーソルオープン
  
    REPEAT -- 繰り返し
        FETCH cur INTO a_id;
        IF NOT done THEN
            -- 通常inset にwhereが使えなかったのでdualを使ってるけどこの辺自信がない...
            INSERT INTO b (ida) SELECT a_id FROM dual WHERE NOT EXISTS(SELECT 1 FROM b WHERE ida=a_id);  
        END IF;
    UNTIL done END REPEAT;
  
    -- クローズ!!
    CLOSE cur;

END
//
CALL p001();-- ここで実行
DROP PROCEDURE IF EXISTS p001;

mysqlでストアドプロシージャ 2012/09/22

mysqlでストアドです。
まずストアドプロシージャのリストを表示は以下で

show procedure status;

参考


まずひながた、これはコマンドに流すsqlを想定しています。
作成してストアドを作成して実行して、実行したプロシージャを削除するというふうにしてます。

実行は以下
$ mysql5 -u root work < test.sql

ひながた。
delimiterの必要性がいまいちわかってないけど。で、以下のコードは信頼性という意味ではちとまずくて、createできて、実行時にエラーがでるとcreateしっぱなしなので、一番最後のdrop procedureが効かないです。

DROP PROCEDURE IF EXISTS p;
delimiter //
CREATE PROCEDURE p()
BEGIN
    SELECT * FROM c;
END
//
CALL p();-- ここで実行
DROP PROCEDURE IF EXISTS p;


参考
oracle 3.2.3 ストアド・プロシージャでのエラー処理

最後に登録されたidを取得します。えーと、auto_incrementされた値をとることができるようです。 2012/09/22

mysqlです。最後に登録されたidを取得します。えーと、auto_incrementされた値をとることができるようです。

select last_insert_id();


参考
auto_incrementでinsertされたレコードを取得するには?
INSERT ~~ ON DUPLICATE 時における、LAST_INSERT_ID()の挙動 - 雑想空間
MySQL :: MySQL 5.1 リファレンスマニュアル :: 11.10.3 情報関数

oracleでいうところのmergeのようなものmysql 2012/03/04

oracleでいうところのmergeと似たようなものがmysqlにないかなーと
ON DUPLICATE KEY UPDATEというのを使えばいいらしい。merge intoよりは使いづらいけど。キーを指定しなおすところがめんどいです。
参考

H2 - afterカラムして追加はできないような....BEFOREしかない 2011/06/26

H2です。
dropしてcreateしてたテーブルですが、データが入ってしまったので、alterしてカラム追加です。

alter_table_add - SQL Grammar

afterカラムして追加はできないような....BEFOREしかないような。

一番最後に追加したい場合は普通にaddするだけなので、afterが文法的になくても困らないけど。

H2で複合キー 2011/04/08

SQL Grammar

H2で複合キーです。

drop table aaa;
create table aaa
(
table_name VARCHAR2(100) ,
a1 VARCHAR2(100),
a2 VARCHAR2(100),
a3 VARCHAR2(100),
a4 VARCHAR2(100),
a5 VARCHAR2(100),
a6 VARCHAR2(100),
a7 VARCHAR2(100),
a8 VARCHAR2(100),
a9 VARCHAR2(100),
a10 VARCHAR2(100),
a11 VARCHAR2(100),
a12 VARCHAR2(100),
a13 VARCHAR2(100),
PRIMARY KEY(table_name, a1),
) ;


参考
H2 Databaseで複合キー|localhost:1981

楽観ハイタ 2011/02/19

DBの楽観排他(ロック)の方法についてです。
まず「楽観的ロックとは」、ですが、とりあえず、がちがちにロックしないという感じでしょうか。
「先に更新したもの勝ち」ぐらいの理解でいいのかな。対称となる悲観的ロックは、ロックとってる間は誰もさわらせないぜぐらいの理解でよろしいか。

で、つぎに、どう実現するのか。
排他したいテーブルに楽観排他用の列を追加します。
考え方は単純に、自分が更新しようとする対象行が、更新するその瞬間に同一であることが保証できればよいわけです。
変更するまえと変更するその瞬間に楽観排他用列が変更されてないことを確認したのち、更新をかけます。
SQLならwhereで同一であることをチェックしつつ、楽観排他用列もちがう値で更新します。

楽観排他列を数値にして、更新するさいにインクリメントするというのが常道っぽいです。で、この楽観排他のための列に時間表現の型を使うってのもある。

おらくる VSIZE は内部でのサイズを返します。 2011/01/30

oracleです。VSIZEはデータベース内部でのサイズを返してくれるそうです。
で、実験です。
下記のようなテーブルを作成します。

create table bbb
(
a1 NCHAR(1),
a2 NVARCHAR2(1),
a3 CHAR(1 BYTE),
a4 CHAR(1 CHAR),
a5 VARCHAR2(1 BYTE),
a6 VARCHAR2(1 CHAR)
)


ちなみにキャラクターセットの確認
select * from nls_database_parameters where parameter ='NLS_CHARACTERSET'


結果は
AL32UTF8


適当にINSERTします。これってどうなると思います?
INSERT INTO bbb VALUES('あ', 'あ', 'あ', 'あ', 'あ', 'あ')


そうですエラーになります。
SQL実行中に以下のエラーが発生しました。
ORA-12899: 列"SYSTEM"."BBB"."A3"の値が大きすぎます(実際: 3、最大: 1)


では、次ぎにどうなるでしょうか。
INSERT INTO bbb VALUES('あ', 'あ', '1', 'あ', '1', 'あ')


微妙にわかりづらいですが、5つめのカラムに全角の"1"を入れようとしてます。

最後にこれで入ります。
INSERT INTO bbb VALUES('あ', 'あ', '1', 'あ', '1', 'あ')

列長セマンティックを指定しているのでBYTEは1バイトしか入らないです。

サイズをみてみます。
select VSIZE(a1), VSIZE(a2), VSIZE(a3), VSIZE(a4), VSIZE(a5), VSIZE(a6) from bbb


わかりずらいけど、下記のようになります。
VSIZE(A1) VSIZE(A2) VSIZE(A3) VSIZE(A4) VSIZE(A5) VSIZE(A6)
2 2 1 3 1 3


NCHARとCHARで何故サイズが違うのか?

[oracle]varcharのデータ型にバイナリを入れることができるか。 2011/01/22

Oracleです。テーブルのデータ型で悩み中です。どういう悩みかというと、char,varchar2のデータ型にバイナリのデータ(ようするに生のデータ)を入るのか、というものです。本来は、そういう用途だと、BLOBとかRAWの型だというのは承知のうえで。

で、入れてそれを使えるのかという実験です。
もしかして、他に方法があるかもしれませんが、結論からいうとできないです。
>_<!

ちなみに環境は、プログラマメモ2: oracleオレオレ開発環境、まずは接続で使ったものと同じです。

NLS_CHARACTERSETは、


select * from nls_database_parameters where parameter ='NLS_CHARACTERSET';
NLS_CHARACTERSET
JA16SJISTILDE

です。

下記ようなテーブルを用意します。

create table aaa
(
code varchar(30),
moji1 varchar(10 BYTE),
moji2 varchar(10 BYTE),
moji3 raw(10),
moji4 blob
);

最初の列に文字コードを指定して、データとしてその文字コードで表現された文字を入れたいというような発想です。
列長セマンティックをよく知らず設定したりしてますが...

検証用コードです。

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class TestDB_moji {

public static void main(String[] args) throws SQLException,
ClassNotFoundException, IOException {
String s = "仲";
aaa("utf8", s);
aaa("sjis", s);
// aaa("jis", s);
}

/*
* 目的 VARCHARの項目にバイナリなデータを保存できるのかな検証
*
* シナリオ 文字コードを指定して,setBytesを使って登録し、とりだして復元できるか調べる
*/
static void aaa(String code, String s) throws SQLException,
IOException {
Connection connection = DriverManager.getConnection(
"jdbc:oracle:thin:@192.168.24.135:1521:test", "system",
"oracle");
// DatabaseMetaData meta = connection.getMetaData();

try {
if(true){
PreparedStatement statement = null;
String q = "INSERT INTO aaa(code, moji1, moji2, moji3, moji4) VALUES(?, ? , ?, ?, ?)";

statement = connection.prepareStatement(q);
// 文字コードで指定したバイト配列
byte[] bs = s.getBytes(code);
// まず普通に入れると
statement.setString(1, code);
statement.setString(2, new String(bs));// varchar2
statement.setBytes(3, bs);// varchar2
statement.setBytes(4, bs);// raw
statement.setBytes(5, bs);// blob
// statement.setBlob(5, new ByteArrayInputStream(bs));// blob
statement.executeUpdate();
connection.commit();
}
// バイトで値入れたら復元できるかな
{
ResultSet resultSet = null;

Statement statement = connection.createStatement();

resultSet = statement.executeQuery("select * from aaa");

int i = 0;
StringBuilder builder = new StringBuilder();
while (resultSet.next()) {
String c = resultSet.getString(1);
builder.append(
String.format(
"%d code:[%s] moi1:[%s] moi2:[%s] moi3:[%s] moi4:[%s]%n",
++i,
resultSet.getString(1), resultSet
.getString(2), new String(resultSet
.getBytes(3), c), new String(
resultSet.getBytes(4), c),
new String(resultSet.getBytes(5), c)));

}
resultSet.close();
statement.close();
System.out.println(builder);
Utils.write(new File("/z/a.txt"), new String(builder).getBytes());
}

} finally {
connection.close();
}

}
}

で、実行します。

こんな感じで登録されてます。


検証のプログラムの実行結果は、

1 code:[utf8] moi1:[莉イ] moi2:[E4BBB2] moi3:[仲] moi4:[仲]
2 code:[sjis] moi1:[仲] moi2:[9287] moi3:[仲] moi4:[仲]


raw,blobはjava側のgetBytesで復元できます。がvarcharでは復元できませんし、文字列になってしまってる...

えーと、一行で複数のテーブルのカウントを返すSQL 2010/06/05

SQLです。SQLは苦手です。
一行で複数のテーブルのカウントを返すSQLです。
見せてもらって、へーと、思ったのでメモ。

実験はH2で行ってます。

まず、テーブル作成

CREATE TABLE A ( id VARCHAR (10))
CREATE TABLE B ( id VARCHAR (10))
CREATE TABLE C ( id VARCHAR (10))

まあ、なんでもいいんだけど。

で、インサートでがんがんデータを入れます。

そしてカウント

select * from (select count(*) as a from A), (select count(*) as b from B), (select count(*) as c from C)


これで3つのテーブルカウントがとれます。

SQLに関しては、たまに絶望的にやる気をなくしてしまうことがあります。
どうしてかなーと考えると、きっと設定が面倒だからかな。