本当にあった怖い話し
DB 編
  プログラミング生放送勉強会 第18回@大阪
  2012/11/03 お だ
自己紹介
織田 信亮(おだ しんすけ)
大阪で開発者しています
SQLWorld の代表です

http://d.hatena.ne.jp/odashinsuke/
Twitter:@shinsukeoda
SQLWorld とは
http://sqlworld.org/
Twitter:@SQLWorld_JP
次のような情報を発信しているコミュニ
ティです
  MS の RDBMS である「SQL Server」
  もちろん他の DB の話しも!
  正規化/モデリング
  SQL/NoSQL
注意書き
特定の製品を挙げているように見えます
が、最近よく使ってるからだけで他意は
ありません!
体験談で公式資料は見つけれてません!
本当にあった怖い話し_DB 編
その1:システム要件を満たしているの
に動かない!
その2:バインド変数が動かない!
ODP.NET 4 がシステム要件を満たしている
のに動かない
http://docs.oracle.com/cd/E16338_01/win.112/b66456/I
nstallSystemRequirements.htm#i1006191
ODP.NET 4 がシステム要件を満たしている
のに動かない
  実行環境
   Windows XP SP3
   .NET Framework 4 Client Profile
   .NET 2系は未インストール
   ODP.NET 4
   標準の Widows Update は適用済み
   .NET 3.5 SP1 は除く

      例外発生!!
using System;
using Oracle.DataAccess.Client;
class Program {
  static void Main(string[] args) {
    try {
      Console.WriteLine(typeof(string).Assembly.FullName);
      Console.WriteLine(typeof(OracleConnection).Assembly.FullName);
      var connStr = “~";
      using (var conn = new OracleConnection(connStr))
      using (var cmd = new OracleCommand(@"select SYSDATE from dual", conn)) {
        conn.Open();
        Console.WriteLine(cmd.ExecuteScalar());
      }
    } catch (Exception e) {
      Console.WriteLine(e.Message);
      Console.WriteLine(e.StackTrace);
    }
    Console.ReadKey();
  }
}
using System;
using Oracle.DataAccess.Client;
class Program {
  static void Main(string[] args) {
    try {
      Console.WriteLine(typeof(string).Assembly.FullName);
                        例外発生!
      Console.WriteLine(typeof(OracleConnection).Assembly.FullName);
      var connStr = “~";
      using (var conn = new OracleConnection(connStr))
      using (var cmd = new OracleCommand(@"select SYSDATE from dual", conn)) {
    --------------------------------------------------------------------------------
        conn.Open();
    mscorelib, Version=4.0.0.0, ...
        Console.WriteLine(cmd.ExecuteScalar());
    Oracle.DataAccess, Version=4.112.3.0, ...
      }
    'Oracle.DataAccess.Client.OracleConnection' type iniitalize
    } catch (Exception e) {
      Console.WriteLine(e.Message);
    error ....
      Console.WriteLine(e.StackTrace);
    --------------------------------------------------------------------------------
    }
    Console.ReadKey();
  }
}
ODP.NET 4 がシステム要件を満たしている
のに動かない
  回避策 (どれか一つでOK)
    Windows Update から .NET 3.5 SP1 をイン
    ストール
    C++ の再頒布可能パッケージ をインストー
    ル
     http://www.microsoft.com/ja-jp/download/details.aspx?id=5638

 OTN Discussion Forums
    ODP.NET4 required .NET2.0?
  https://forums.oracle.com/forums/thread.jspa?thr
  eadID=2423728&stqc=true
バインド変数に長い文字列を指定すると動
かない
 某9i で発生!
  10g, 11g では発生せず
バインド変数の値が 2000文字(ASCII) 以
上の物が複数存在するとエラーが発生

ORA-01461:
LONG値はLONG列にのみバインドできま
す。
create table TEST (
  PK number(3,0) not null primary key,
  CONTENT1 varchar2(4000),
  CONTENT2 varchar2(4000),
  CONTENT3 varchar2(4000)
)
/
insert into TEST values (:PK, :CONTENT1, :CONTENT2, :CONTENT3)
/
drop table TEST
/
    結果            CONTENT1      CONTENT2      CONTENT3
    NG            4000文字        4000文字        4000文字
    NG            2000文字        2000文字        0文字
    OK            1999文字        2000文字        1999文字
    OK            1999文字        4000文字        1999文字
バインド変数に長い文字列を指定すると動
かない
 回避策
  9i を使わない
  クエリ内で文字列結合を行い、1バインド変
  数の文字数が大きくならないようにする
create table TEST (
  PK number(3,0) not null primary key,
  CONTENT1 varchar2(4000),
  CONTENT2 varchar2(4000),
  CONTENT3 varchar2(4000)
)
/
insert into TEST values (
  :PK,
  :CONTENT1_1 || :CONTENT1_2 || :CONTENT1_3 || :CONTENT1_4,
  :CONTENT2_1 || :CONTENT2_2 || :CONTENT2_3 || :CONTENT2_4,
  :CONTENT3_1 || :CONTENT3_2 || :CONTENT3_3 || :CONTENT3_4
)
/
drop table TEST
/
まとめ
変な書き方してるクエリを見つけても、
バカにしない!
意味の分からない事象にぶち当たっても
泣かない!
ご清聴ありがとうございました
本当にあった怖い話し_DB 編
おまけ:ストアド × ファンクションで
…
本当にあった怖い話し_DB 編
おまけ:ストアド × ファンクションで
…



   ┌(┌^o^)┐ホモォ...
本当にあった怖い話し_DB 編
おまけ:ストアド × ファンクションで
…



   ┌(┌^o^)┐ホモォ...
ストアド内で複数回呼び出しているファン
クションのトランザクションが異なる
 11g で発生
 非常に複雑なストアド内で同じファンク
 ションを何回も参照している時に発生
  cursor
  共通テーブル式
  with cte as (select ~)
  window 関数
  sum() over (partition by ~)
  union all
サンプルクエリの説明
FOO テーブル
   マスタテーブル
BAR_FUNC
   FOO.HOGE_KBN が ‘1’ はエラー発生
   FOO.HOGE_KBN が ‘2’ はエラーなし
ZOO_PROCEDURE
   FOO、BAR_FUNC を参照
   トランザクションテーブルを更新
   FOO テーブルは更新しない
FOO テーブルのデータ   BAR_FUNC との関係
HOGE_KBN
‘1’            エラー
‘2’            エラーなし

BAR_FUNC では検証用に
RAISE_APPLICATION_ERROR
を使ってエラーを発生させている
-- FOO.HOGE_KBN = ‘1’ はエラー発生、’2’ はエラー発生せず
update FOO set HOGE_KBN = '1'
/
commit
/
select BAR_FUNC(~) from dual -- FOO.HOGE_KBN = ‘1’ なのでエラー発生
/
update FOO set HOGE_KBN = '2'
/
select BAR_FUNC(~) from dual -- FOO.HOGE_KBN = ‘2’ なのでエラー発生せず
/
rollback
/
call ZOO_PROCEDURE (~) -- FOO.HOGE_KBN = ‘1’ なのでエラー発生
/
update FOO set HOGE_KBN = '2'
/
call ZOO_PROCEDURE (~) -- FOO.HOGE_KBN = ‘2’ なのにエラー発生!!
/
ストアド内で複数回呼び出しているファン
クションのトランザクションが異なる
 BAR_FUNC 内で、FOO テーブルのデータを
 確認してみると…
   エラーが起きる時は、何故か更新前のデー
   タを参照している

  同一トランザクション内で更新したはず
  の
  データが見れてない!
ストアド内で複数回呼び出しているファン
クションのトランザクションが異なる
 cursor と 共通テーブル式を辞めても発
 生
 window 関数か union all を辞めると発
 生しなかった
 window 関数と union all を使ったシン
 プルなストアドだと発生しなかった
  再現するミニマムクエリを発見出来ず…
ストアド内で複数回呼び出しているファン
クションのトランザクションが異なる
 回避策
  window 関数は削れなかったので、union
  all を諦めて、2つのクエリに分けた。
  本当に回避出来てるのか不明。
  原因を探れていないので、本当に発生しないのか
  は不明。
  テストでは再現しなかった。
まとめ
変な書き方してるクエリを見つけても、
バカにしない!
テストは大事!
意味の分からない事象にぶち当たっても
泣かない!
ご清聴ありがとうございました

本当にあった怖い話し Db編