Technology へようこそ
ここは技術者の「経験」と「ノウハウ」のブログです
2010年06月28日 |
カーソルループの高速化(2) |
前回、current of を使用して高速化を図る方法について書いたのですが、 ここにヒントがあります。 前回のサンプルを見ていただくとわかるのですが、 update文が非常にシンプルな形になっていると思います。 カーソルループ中で実行されるSQL文をいかに簡単な形にするか、 という観点が比較的有効です。 つまり、join を最小限にし、キー項目をwhere節で指定して やることなんですね。つまり、 insert文は、select句を使わずvalue句を使う。 update、delete文は、current ofを使う。 ということです。 具体的には、 カーソル宣言で、ループ内実行SQL文で使用する項目を定義し、フェッチする。 フェッチした変数を使用してSQL文を実行する。 ということです。 declare cursor curTest これにより、カーソルオープン時のコストは多少かかりますが、 join時のコストが削減されます。 カーソルオープンは初回1回のみですが、joinは処理件数分行われますので、 高速化が可能になるというわけです。 しかし、これでカーソルループを使用しない処理よりも高速化が可能か、というと、 残念ながらそうではありません。 例えば、1万件を処理する場合、処理の内容にもよりますので、一概には言えないのですが、 カーソルループ使用:約160秒 カーソルループ未使用:約10秒 くらいの差がでてしまいます。 結局、繰り返しの処理は単純に1回の処理時間×処理件数になってしまいますので、 1回の処理時間をいかにはやくするか?という視点で高速化を行うわけですが、 やはり限界があるわけで・・・。 できるだけ繰り返しを使用せずに処理を記述することが、性能問題には有効です。 [ posted by H.K ]
スポンサーサイト
|
2010年06月14日 |
カーソルループの高速化(1) |
前々回、「ループ処理内で連番を振る」テーマについて書いたのですが、 「カーソルループ自体を高速化したい」という要望があったりします。 処理ロジックは変えたくない、でも速くしたい。 問題の本質は、多くの場合、性能試験がシステムテストの一部になっていることでしょう。 システムテストに入る時点で機能テストは終わっているわけですから、 処理ロジックは変えたくない、という意識が働くのはあたりまえのことです。 しかし、性能問題が発現した場合、その多くは「処理ロジックの大幅変更」を 伴うことが多いものです。 今回はカーソルループを残したままでの高速化を考えた場合、 どういう方法があるのか?ということで、「ループ処理を可能な限り排除する」という 本来の高速化の指針から言えば、少々例外的なトピックになってしまいますが・・・。 もちろん、大幅な変更ではないにしろ、ある程度の変更は必要になります。 カーソルループの処理を高速化するのに一番最初に浮かぶのは カーソル自体を速くできないか?ということです。 T-SQLのカーソルにはいろいろなオプションがありますが、 あまり効果は期待薄。・・・。強いて言うなら、カーソル属性をreadonly にせず、 current of を使って更新するようなロジックを組むこと。 ま、これはカーソル対象テーブルを更新する場合とかにしか使えませんが。 declare cursor curTest あとはループのなかで行われているSQL文の最適化。 インデックスを試したり、実行プランを解析してjoin の条件順序の変更や ヒントを付与したりして調整していくのですが、 多くの場合、なかなか性能は上がらないものです。 では、これ以上、何ができるのか?次回。 [ posted by H.K ]
|
2010年05月07日 |
クエリ文字列結合の罠 |
SQLServer2005以降、nvarchar/varchar 型のサイズとして「max」が指定できるようになりました。 それまでは最大で4000文字(varcharは8000文字)という制限があり、ストアドプロシージャなどで長めの動的クエリを記述する場合、ひとつの変数にはクエリ文字列が収まりきらず、仕方なく複数のクエリ格納用変数を使って用意して、実行時に文字列結合、なんていう技を使っていたりしたものですが、「max」登場により、そういった見苦しいコードを書かなくて済むようになったかと思いきや、コトはそう単純な話ではなかったようです。 そう、たしかに格納先変数のサイズ制限自体は緩くなってはいるのですが、値をセットする際の「リテラル文字列自体の長さ」については従来通りの制限があるのです。 つまり、4000文字(あるいは8000文字)の文字列を一気に変数にセットできない、ということ。 ちなみにマイクロソフトはこの仕様について変更する気はなさげ。 もちろん、従来通りリテラル文字列を分割して順次代入していく形にすれば回避はできますが、やはりどう考えても見苦しいうえに面倒臭い。 もう少しスマートに回避する方法はないかと思いきや、 文字列連結のリファレンスにこんな記述を発見。 文字列の連結の結果が8,000バイトを超える場合、結果は切り捨てられます。というわけで、試してみたところ、下記「OK Case」のように、単純にmaxサイズの文字型変数を結合してやれば良いことが判明。 これならほとんど違和感なく記述できそうです、というより これはもはやコーディング規約にしてしまったほうが良いかもしれない世界。 declare @sql nvarchar(max) [ posted by ken ]
|
2010年02月12日 |
曜日の名前 |
VB 2005で作成したWindowsフォームアプリの多言語対応化の作業をしています。 アプリではSQL Server 2005のデータベースからの情報を Crystal Reports for VB 2005より帳票出力しています。 多言語対応化として幾つか対応事項となった中の一つに曜日の取扱い方がありました。 帳票上に日付項目(表示形式"2010/02/12(金)10:00")があり、 そのまま中国OS環境上で実行した場合は"2010/02/12(星)09:00"となりました。 これは単にSQL Serverのストアドプロシージャで強制的に曜日の名前より 先頭1文字を取っている為、中国OS環境では曜日の名前が 「星期一、星期二、星期三、星期四、星期五、星期六、星期日」となり いずれの曜日でも先頭1文字で"星"となったというオチです。 そもそも曜日の名前をそのまま表示できる仕様であれば問題なさそうですが 日本では"(金)"などの表示形式は往々にしてありそうなものです。 対応方法としてまずCrystal Reports の帳票デザイン上にて日付項目の式フィールド内に 関数WeekDayName(曜日,省略タイプ)を用いて編集という方法を試みました、 結果は日本OS環境では"金"となりましたが中国OS環境では"星期五"となってしまい断念、 今回はSQL Server側からストアドプロシージャ内にて以下のような対応方法を採用しました。 -- 現在の Microsoft Windows のロケール ID 取得 [ posted by kami ]
|
2010年01月29日 |
tempdb |
T-SQLの性能試験。大量データ発生が予想される処理での性能問題は フェーズが後になればなるほど費用がかかります。当社ではできるだ け単体テスト時にこの芽を摘むようにしているのですが、数万件を ターゲットにテストを開始。1分足らずで終了。1件あたり2ミリ秒。 まあまあですか。と安心していたのもつかの間、5万件に件数を上げた ところで異常終了。あらら、バグですか! 調査してみたものの、それらしいバグは見つからず調査は暗礁に。 時を同じくして名古屋の方も「10万件でテストすると突然異常終了 するんですよ。」と連絡が。 同じ処理をテストしているわけではないんですが、なにか共通性が あるのでは?ということで環境周りを調査。 すると、tempdbが肥大化してHDDを圧迫しているではないですか。 なんとそのサイズ120GB! 一旦、インスタンスを停止して再起動(tempdbがリセットされる) 再度、処理を実行してエクスプローラを観察していると見る間に ディスクの空きが無くなっていくではないですか! うーん、こりゃ困ったなあ・・・。どう対処するか。 名古屋から「ここのSQLが異常に遅いことがわかりました」という 連絡があり、そのSQLの実行プランを見てみると件数から見て明らか にLOOP JOIN を使うべきところにHASH JOINが使用されているでは ないですか! 早速、"JOIN" キーワードを "INNER LOOP JOIN" に変更して実行。 1分20秒で何事もなく終了。tempdbのサイズはたったの数百MB! こんなことってあるんですねえ!? [ posted by H.K ]
|
2010年01月05日 |
桁落ち |
SQL Serverで、除算した結果の少数点以下5桁目を 端数処理するという処理をしておりました。 ところが、思ったような結果にならないので調べたところ 原因が判明しました。 計算は、A(money型)÷B(money型)だったのですが、 実はmoney型は少数点以下4桁までしか保証されず、 計算においても同様に4桁までの保証となります。 AとBを、decimalにcastすることで回避できましたが、 どちらか一方でもcastすれば、大丈夫です。 [ posted by S.S ]
|
2009年12月28日 |
月末・月初 |
ある日付の月末を求めます。 ちなみに、なぜこんなことを書いたかと言いますと、 SQL Server2005には、月末を求める関数がない為です。 ということで、考えてみました。 例1)
例2)
上記の例2)では各項目のデータ型が数値ということもあり、 少々長ったらしい書き方にはなっていますが、理想はもっとスマートに書きたいものです。 (ブログ上、改行されてしまいました・・・) 月の初めは必ず【1日】ですが、文字列連結でわざわざ'01'と 指定しなければいけないのが、一番残念です・・・ [ posted by T.K. ]
|
2009年12月18日 |
変更データ キャプチャ |
SQL Server 2008では、UPDATEやINSERT、DELETEステートメントによる更新履歴を保管できる 変更データ キャプチャ機能があります。指定した時間の過去のデータを参照したり、 オペレーションミス時のデータ回復などで利用できます。 機能を使用する場合、SQL Server Agentサービスを開始しておく必要があります。 例) -- データベース作成サンプルテーブルのデータを編集 -- col1 = 1 の行を更新 ( 'test' → '更新した行' )サンプルテーブルのデータ内容 col1 col2cdc スキーマにあるdbo_sample_tbl_CT(スキーマ名_テーブル名_CT)を確認 select * from cdc.dbo_sample_tbl_CT;operation列の内容から更新前データ(3)、更新後データ(4)、追加(2)、削除(1)と確認できます。 テスト時などに利用できそうな機能です。 [ posted by kami ]
|
2009年10月30日 |
透過的なデータ暗号化 |
SQL Server 2008の透過的なデータ暗号化(TDE)は、データベースに対して暗号化を設定するだけでデータベース内のすべてのオブジェクトを暗号化できます。 アプリケーションからは透過的に利用でき、SQL Server 2005と比べアプリケーションの修正いらずと便利です。 また、バックアップファイルも自動的に暗号化(データファイル全体を暗号化)されます。 設定例) -- サンプルデータベース(sample_db)を作成別マシンへデータを移動する場合 -- データベースのバックアップ作成備えあれば憂いなしと言います。 セキュリティー関連技術も覚えておくと今後に役立つかもしれません。 [ posted by kami ]
|
|