【SQL Server】SQLのパフォーマンスを向上させるためにインデックスを活用する!

SQLのパフォーマンス

業務でSQLの性能向上をしましたので、SQLのパフォーマンスを上げるためのテクニックをまとめました!

まずは、SQLのチューニングをする前に開発段階で性能を向上させるためのノウハウです。

インデックスを活用する

SQLにはインデックスがついております。

膨大なデータから特定の条件のデータ(WHERE句で指定したデータなど)を検索する際にインデックスを活用することで処理が速くなります。

イメージとして、英語辞典で単語を調べる際に、1ページからパラパラめくって調べるのではなく、索引を用いて該当するページを開くような役割をしてくれています。

このようにインデックスを活用した検索方法をSeek(シーク)といいます。

一方で、テーブルデータを先頭から順番に1つずつ該当するデータか探すような検索方法をScanといいます。

 

SQL Serverでは3種類のインデックスがあります。

1. クラスター化インデックス
2. 非クラスターインデックス
3. 付加列インデックス

 

1.クラスター化インデックス

クラスター化インデックスはルートノード、中間ノード、リーフノードの各ノードを持つ構造を取っています。

現在、社員ID、社員名、部署をカラムとして持つテーブルがあると想定し、社員IDにクラスター化インデックスを貼ると以下のような構造を取ります。

ルートノードには、社員ID10000ずつに対応したインデックスファイルがあり、さらにインデックスファイルは中間ノードと紐づいています。中間ノードには、社員ID1000ずつに対応したインデックスファイルがあり、それらがリーフノードと紐づいています。

リーフノードには、実データが格納されており実際の社員ID、社員名、部署のデータが存在しています。

 

クラスター化インデックスの構造

今、SELECT * FROM 社員テーブル WHERE ID=10003というクエリを実行します。

すると以下の手順で実データを取得します。

1.ルートノードを参照し、ID=10003に該当するインデックスファイルを特定する。(この場合、インデックスファイルBが該当する)

2.インデックスファイルBを参照し、ID=10003に該当するインデックスファイルを特定する。

3.インデックスファイルBAを参照し、ID=10003に該当する実データを取り出す。

クエリの実行

 

 

2.非クラスター化インデックス

クラスター化インデックスとは別にSQL Serverでは非クラスター化インデックスと呼ばれるインデックスもあります。

こちらは、次のような構成になっています。

 

 

 

【ASP .NET】Postmanを用いてWeb APIをデバッグしたときに発生したエラーについて

エラー内容

Postmanを使ってASP .NET Web APIをローカル環境でデバッグを使用としたら、以下のエラーが発生した。

エラーメッセージ:Could not get response エラー種類:Error: read ECONNRESET

Postmanエラー

原因

原因としては、デバッグ環境がhttps通信であったためサーバー証明書関連でエラーが発生しているらしい。 ローカルでの開発であるため、ひとまずhttp通信のポートでデバッグすれば上記のエラーは出なくなる。

http通信のポートへ切り替えるには、.vsフォルダ内にあるconfigファイルを開き、http通信用のポートへアクセスすればいい。

タグで囲われた箇所がhttpとhttpsのローカルホストのポートを指定しているため、今回はhttp用ポートである63453を使用した。

            <site name="DRMDataManager" id="2">
                <application path="/" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="\Work\Demo\DaishiRetailManager\DRMDataManager\DRMDataManager" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation="*:63453:localhost" />
                    <binding protocol="https" bindingInformation="*:44386:localhost" />
                </bindings>
            </site>

【SQL】NULL値を含んだ条件式

NULL値を含んだ条件式

先日、現場で単体テスト中にNULL値の取り扱いによるバグが見つかったため、備忘録としてNULL値を含む正確な条件式を整理します。

バグの内容は省きますが、条件式にNULLが入っていたため出力結果が何も得られなく意図しない動作を起こしていました。

 

条件式では、結果としてTRUE, FALSEの2値を取り扱うのが一般的です。ただし、データベース操作などにおいてはTRUE, FALSE, NULLの3値のいずれかが結果として取り扱われます。

このNULLを結果として扱うに違和感を感じるかもしれませんが、そういうものと割り切ってしまいましょう!

 

NULLの条件式の結果

NULLを含んだ条件式を以下に示します。

特にNULL AND TRUEがNULLになったり、NULL OR TRUEがTRUEになったりは感覚的に受け入れられるものの、赤字部分の条件式は判定がどちらになるのか毎回調べています。

TURE, FALSE, NULLの評価の優先度?(適切な言葉が分からないためこのように表しています。)としては、

TRUE > NULL > FALSEの順に強弱関係?があると考えることで条件式の結果を感覚的に受け入れやすくなります。

NULL OR FALSEの場合はNULLはFALSEより評価されるため、結果はNULLになります。

逆にFALSE AND FALSEの場合はFALSEがNULLより評価されるため、結果はFALSEになります。

 

 

 

条件式

 

C#でドメイン駆動開発パートを受講して

 

BADコードの作成

本コースでは、まずBADコード(保守性が低いコード)を作成し、どのコードがなぜBADコードなのかの解説します。

BADコードであることを理解した上でより保守性が高いコードの実装方法をハンズオン形式で学びます。

 

なぜ共通関数クラス(Commonクラス)はBADコードなのか

ここでは、単位や接続文字列などの定数をまとめたCommonConstクラスと温度を引数で指定された桁でまとめて文字列へ返す関数があるCommonFuncクラスを実装し、業務ロジック「温度を小数点2桁に丸めて単位℃をつける」RoundStringメソッドを実装しました。

 

CommonFuncクラス

 

さまざまなクラスから呼び出される共通関数(Common)は潜在的なBADコードです。(実際にこれまで携わった現場でCommonと名の付く共通関数クラスは見てきました。)

 

理由としては、温度を2桁で丸めて単位℃をつける際はCommonFuncクラスのRoundStringメソッドを使わなければならないことを覚えておかなければならないからです。

関数の存在を知らないメンバーが同じようなコードを書き出したり、画面実装コードに直接℃を書き込む人も現れたりと同じような業務ロジックが散在しがちになります。

 

テストコードとは何か

 

テスト駆動開発

テスト駆動開発では、先に失敗するテストコードを実装し、後からテストコードが問題なく成功するように実装していきます。テストエクスプローラーから分かるようにテストを実行し、失敗すると該当メソッドが赤くなります。

 

サンプルテストコード


実例として、WeatherLatestViewModel.csのテストコードWeatherLatestViewModelTest.csを先に書きます。この時点では、WeatherLatestViewModelは実装せずに、先にテストコードのみを実装しテストを実施します。

 

WeatherLatestViewModelTestのテストコード

 

テストコードがエラーとなるため、WeatherLatestViewModelを実装します。この段階でもテストコードはエラーとなります。

 

WeatherLatestViewModelのコード


string.Emptyで初期化する実装を加えて、テストコードを成功させます。

WeatherLatestViewModelのコード02

 

 

 

 

メンバーシップフレームワーク

メンバーシップフレームワーク

メンバーシップフレームワークとは、asp.netによるログイン認証機能のフレームワーク

 

メンバーシッププロバイダークラス

認証機能を持つメンバーシッププロバイダークラスをModelフォルダ内に実装する。

MembershipProviderクラスを継承したCustomMembershipProviderクラスを作成し、抽象メソッドを実装する

f:id:daishi-sakai:20210814093929p:plain

CustomMembershipProviderクラス

ユーザー名とパスワードを受け取り正しいかをbool型で返すメソッドValidateUser()を実装する

 

f:id:daishi-sakai:20210814094503p:plain

ValidateUserメソッド

認可を表すロールプロバイダークラスCustomRoleProviderクラスを実装する。

RoleProviderクラスを継承し、抽象メソッドを実装する。

f:id:daishi-sakai:20210814094658p:plain

CustomRoleProviderクラス

GetRolesForUser()

指定されたユーザーが持つロールをstring[]型で返すメソッド

f:id:daishi-sakai:20210814095011p:plain

GetRolesForUserメソッド

IsUserInRole()

引数で与えられたユーザーが、引数で与えられたロールの権限を所持しているかbool型で返すメソッド

f:id:daishi-sakai:20210814095415p:plain

IsUserInRoleメソッド

 LoginViewModeloの作成

ViewModelとは、実際のデータベースに登録されているデータと画面上でのみ用いるデータの差異を解消するためのデータトランスファークラスを指す。

ユーザー名とパスワードを入力するログイン画面を構成する。

それぞれ必須項目であるため[Required]を設定する。

また、パスワードは[DataType(DataType.Password)]とすることで入力文字を*********形式で表示させる。

f:id:daishi-sakai:20210815110203p:plain

LoginViewModel

 

LoginController 

LoginViewModelクラスから受け取ったユーザー名、パスワードを処理するコントローラークラスを実装する。

 

[AllowAnonymous]によりログイン認証前にアクセスできるように設定する。

ログイン認証のメソッドを持つCustomMembershipProviderクラスのインスタンスを生成する。

f:id:daishi-sakai:20210815111404p:plain

LoginControllerクラス

ログイン情報が入力されたとき(POST)を処理する。
CSRF対策として、[ValidateAntiFogeryToken]アノテーションを付加する。

また、入力情報に対して、ValidateUser()を用いてユーザー名とパスワードが正しいかチェックする。

正しい場合、FormsAuthentication.SetAuthCookie()を用いる。

指定したユーザー名に対して、認証チケットを発行しCookieに保存する。

f:id:daishi-sakai:20210815112352p:plain

LoginControllerクラス2

Login用のView 

Login用のViewを作成し、ログイン失敗時のエラーメッセージをViewBagで表示する。

f:id:daishi-sakai:20210817063601p:plain

ViewBag

共通レイアウトにログイン時にサインアウトできるようリンクを設定する。

Request.IsAuthentificated:ログインしているとき、はtrueの状態になる。ログインしていないとき、falseの状態になる。

@Html.ActionLink("表示名", "アクションメソッド", "コントローラ名")

コントローラ名のアクションメソッドへ遷移する。

f:id:daishi-sakai:20210817063815p:plain

共通レイアウトにSignOutを実装する

 

Authorizeを設定することでTodoesControllerにはサインインしている状態ではないとアクセスできなくなる。

f:id:daishi-sakai:20210821110126p:plain

Authorize


 

 

内部結合と外部結合

内部結合

select [テーブル名1.列名, テーブル名2.列名] from テーブル名1 inner join テーブル名2 on テーブル名1.列名 = テーブル名2.列名;

 

f:id:daishi-sakai:20210828111356p:plain

内部結合

usersテーブルのprefecture_idとprefectureテーブルのidを結合させている。

 

 

外部結合

 

 

 

 

SQL LocalDBの削除・再作成

SQL LocalDBの削除・再作成

asp.net MVCでTodoアプリを作成していた際中に、sqlのlocalDBの環境を破壊してまった。

アプリを再作成やsql windows sereverを再インストールしても復元できず、原因が分からなかったため、localDBの削除・再作成を実施した。

下記のフォルダにcode firstで生成したlocaldbインスタンスが残っていたため、こちらのmssqllocaldbを削除し問題なく復元ができた。

C:\Users\{XXXXXX}\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances

f:id:daishi-sakai:20210807110239p:plain

localDBインスタンス