Code for final

ふぁいなる向けのコード置き場です。すでにコードじゃないこともいっぱい。

Xamarinで空プロジェクトをビルドしただけなのに「"ResolveLibraryProjectImports" タスクが予期せずに失敗しました。」

久々にXamarinを使ってみようと空のプロジェクトを作成し、ビルドしただけなのにエラー。

え、なんで?

"ResolveLibraryProjectImports" タスクが予期せずに失敗しました。

って何。わけわからん。Xamarinってやっぱ素人には無理なのか。

と思ってエラーの詳細を見ると

System.IO.PathTooLongException: 指定されたパス、ファイル名、またはその両方が長すぎます。完全限定型名は 260 文字未満で指定し、ディレクトリ名は 248 未満で指定してください。

というのを見て、ピーンときた。

昔、Xamarinのセミナーで講師のひとがプロジェクトはCドライブの直下の短い名前のフォルダに短い名前のソリューションにしないといけないみたいなことを言われてたなー。

ということでパスが短くなるようにディレクトリも浅い場所にしてソリューション名も短くしたら普通にビルドが成功しました。

SQL ServerでManagement Studioでは速いのにWEBアプリでのみSQLの実行が遅い場合に確認すること。

ついこないだSQL Serverのユーザファンクションの性能問題について書きましたが、実はそのあとも性能問題が発生しました。

final.hateblo.jp

Javaを使ったWEBアプリで検索処理が20secくらいかかるという問題が発生しました。

SQLが悪いと疑ってSQLログからSQLを抽出し、SQLをManagement Studioで実行しましたが、結果は2secで返ってきて問題はありませんでした。

ググるといろいろ実行計画のキャッシュを削除すればいいとかいろいろ試したのですが、さっぱりでした。

困ったときはプロファイラということでプロファイラを実行したあとにWEBアプリを実行すると以下のようなSQLが実行されていることがわかりました。

declare @p1 int
set @p1=10
exec sp_prepare @p1 output,N'@P0 int,@P1 nvarchar(4000),@P2 nvarchar(4000)'
,N'SELECT * FROM SCHEDULE WITH (NOLOCK) WHERE RoomNo = @P0  AND STATUS =  @P1)',1
select @p1

使っているWEBアプリはHibernateを使っていましたが、普通のSQLSQLが実行されていると思っていたので驚きました。

それでプロファイラから抽出したSQL(@p1=nullに変更する必要があります)をManagement Studioで実行したのですが、結果は2secで返ってきました。

WEBアプリが実行されているのを忠実に再現してみようと考えて、eclipseのDBViewrから抽出したSQLを実行したところ、20secで返るようになり、再現しました。

JDBCについて調べたところ、どうやらSQLServerJDBCドライバはデフォルトでUnicodeで処理されるようです。

SQL ServerではvarcharがSJIS、nvarcharがUnicodeなので、SQLがnvarcharとして実行されており、テーブルのカラムの型がvarcharだったので暗黙的な変換が行われるため、遅くなっているということがわかりました。

いくら暗黙的変換と言えども遅すぎな気はしますが。

対策として以下の2つが考えられます。

  • SQLのほうでキャスト(キャスト処理に若干時間がかかるが、暗黙的変換の比ではない)
  • JDBCの接続プロパティ「sendStringParametersAsUnicode」をfalseにする。(デフォルトはtrue)

接続プロパティについては以下を参照。
接続プロパティの設定 | Microsoft Docs

近年、Unicode対応が進んでいるので、デフォルト値としては当然かもしれませんが、昔からある大企業の基幹システムなどホストと連携したりしているシステムは いまだにSJISで固められていたりします。(規約でnvarcharの使用禁止されている)
今回もそんなシステムでした。
なので接続プロパティを変更する方法で対応しました。
nvarcharがほとんどで一部のテーブルだけSJISになっているのであればキャストで対応ですね。

この問題、わりとメジャーっぽいんですが、SQL ServerでしかもSJISで縛られているシステムを開発したことなかったのでわかりませんでした。

また、開発環境ではデータベースの構築時の設定(詳しくは調べてないですが、照合順序かと)が検証環境と異なっていたらしくこの問題が起きていませんでした。

今回の事象はSQL Serverのみで起こる問題でしたが、
SQLの性能を調査する際は、プロファイラで実行されているSQLを抽出し、WEBアプリがJDBC接続だったらJDBC接続で確認するなど接続方法、データベース設定を発生する環境と同じにして再現確認するのが重要!と実感しました。

SQL Serverのユーザファンクション(ユーザー定義テーブル値関数)を使用していて性能が出ない(遅い)場合にすること。

SQL Server上の開発したシステムでSQLの共通化(複雑な部分の隠蔽)にユーザファンクション(ユーザー定義テーブル値関数)を使用しました。

ユーザー定義テーブル値関数 | Microsoft Docs

ユーザファンションは大抵は計算式とかで使うと思いますが、SQL Serverではテーブルを返すこともできるのです。
ビューではパラメタを渡せませんが、ファンクションなのでパラメタを渡すことができて便利です。

通化はうまく作用してSQLのスキルが低い開発者でも開発ができて非常に有用でした。

ところが、結合テストでデータ件数が10,000件を超えると途端に実行速度が遅くなりました。
件数が少ないときは数秒だったのに、実行しても全然返ってきません。

いろいろ調べるとユーザファンクションとテーブルのJOINでは特に問題が確認できませんでしたが、 ユーザファンクション同士をJOINしているところが問題で、 どうやらインデックスが効いていないっぽいことがわかりました。

対策として試しにユーザファンクションのところを、ユーザファンクション内で定義しているSQLに置き換えることで性能が改善しました。

ただ、ユーザファンクションで定義しているSQLを展開しただけなので、実行されるSQLとしては同じものになると思っていたのですが、若干違うみたいです。

まとめるとユーザファンクション(ユーザー定義テーブル値関数)を使用して性能が出ない場合は、ユーザファンクション同士をJOINなどしていないか確認し、していたらユーザファンクションで定義しているSQLをそのまま置き換えると性能が改善すると思います。