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

28
本本本本本本本本本本 DB 本 プププププププププププププ プ 18 プ @ ププ 2012/11/03 プ プ

Upload: oda-shinsuke

Post on 24-May-2015

1.702 views

Category:

Technology


1 download

DESCRIPTION

プログラミング生放送勉強会 第18回@大阪

TRANSCRIPT

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

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

プログラミング生放送勉強会 第 18 回 @ 大阪2012/11/03 お だ

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

自己紹介

織田 信亮 ( おだ しんすけ )大阪で開発者していますSQLWorld の代表です

http://d.hatena.ne.jp/odashinsuke/Twitter:@shinsukeoda

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

SQLWorld とは

http://sqlworld.org/Twitter:@SQLWorld_JP次のような情報を発信しているコミュニティです

MS の RDBMS である「 SQL Server 」もちろん他の DB の話しも!正規化 / モデリングSQL/NoSQL

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

注意書き

特定の製品を挙げているように見えますが、最近よく使ってるからだけで他意はありません!体験談で公式資料は見つけれてません!

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

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

その1:システム要件を満たしているのに動かない!その2:バインド変数が動かない!

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

ODP.NET 4 がシステム要件を満たしているのに動かない

実行環境Windows XP SP3.NET Framework 4 Client Profile.NET 2 系は未インストール

ODP.NET 4標準の Widows Update は適用済み.NET 3.5 SP1 は除く

例外発生!!

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

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();  }} 

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

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();  }} 

例外発生!--------------------------------------------------------------------------------mscorelib, Version=4.0.0.0, ...Oracle.DataAccess, Version=4.112.3.0, ...'Oracle.DataAccess.Client.OracleConnection' type iniitalize error ....--------------------------------------------------------------------------------

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

ODP.NET 4 がシステム要件を満たしているのに動かない

回避策 ( どれか一つで OK)Windows Update から .NET 3.5 SP1 をインストールC++ の再頒布可能パッケージ をインストール

http://www.microsoft.com/ja-jp/download/details.aspx?id=5638

OTN Discussion ForumsODP.NET4 required .NET2.0?

https://forums.oracle.com/forums/thread.jspa?threadID=2423728&stqc=true

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

バインド変数に長い文字列を指定すると動かない

某 9i で発生!10g, 11g では発生せず

バインド変数の値が 2000 文字 (ASCII) 以上の物が複数存在するとエラーが発生

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

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

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 文字

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

バインド変数に長い文字列を指定すると動かない

回避策9i を使わないクエリ内で文字列結合を行い、 1 バインド変数の文字数が大きくならないようにする

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

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/ 

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

まとめ

変な書き方してるクエリを見つけても、バカにしない!意味の分からない事象にぶち当たっても泣かない!

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

ご清聴ありがとうございました

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

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

おまけ:ストアド × ファンクションで…

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

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

おまけ:ストアド × ファンクションで…

┌(┌^o^)┐ ホモォ ...

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

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

おまけ:ストアド × ファンクションで…

┌(┌^o^)┐ ホモォ ...

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

ストアド内で複数回呼び出しているファンクションのトランザクションが異なる

11g で発生非常に複雑なストアド内で同じファンクションを何回も参照している時に発生

cursor共通テーブル式with cte as (select ~)

window 関数 sum() over (partition by ~ )

union all

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

サンプルクエリの説明

FOO テーブルマスタテーブル

BAR_FUNCFOO.HOGE_KBN が ‘ 1’ はエラー発生FOO.HOGE_KBN が ‘ 2’ はエラーなし

ZOO_PROCEDUREFOO 、 BAR_FUNC を参照トランザクションテーブルを更新FOO テーブルは更新しない

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

FOO テーブルのデータ BAR_FUNC との関係HOGE_KBN‘1’ エラー‘2’ エラーなし

BAR_FUNC  では検証用に RAISE_APPLICATION_ERROR を使ってエラーを発生させている

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

-- 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’  なのにエラー発生!! / 

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

ストアド内で複数回呼び出しているファンクションのトランザクションが異なる

BAR_FUNC 内で、 FOO テーブルのデータを確認してみると…

エラーが起きる時は、何故か更新前のデータを参照している

同一トランザクション内で更新したはずのデータが見れてない!

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

ストアド内で複数回呼び出しているファンクションのトランザクションが異なる

cursor と 共通テーブル式を辞めても発生window 関数か union all を辞めると発生しなかったw indow 関数と union all を使ったシンプルなストアドだと発生しなかった再現するミニマムクエリを発見出来ず…

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

ストアド内で複数回呼び出しているファンクションのトランザクションが異なる

回避策window 関数は削れなかったので、 union all を諦めて、 2 つのクエリに分けた。本当に回避出来てるのか不明。原因を探れていないので、本当に発生しないのかは不明。テストでは再現しなかった。

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

まとめ

変な書き方してるクエリを見つけても、バカにしない!テストは大事!意味の分からない事象にぶち当たっても泣かない!

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

ご清聴ありがとうございました