bxdxmx3

きじれてじろあ なきがせすで あぷせとねでぶ

試してみた。 〜 Can Foreign key improve performance?

Can Foreign key improve performance?: SQL Server 2000, 2005, 2008

外部キーを設定することで、SELECTパフォーマンスが上がります、とのこと。

SQL SERVER 2008で検証。

実行環境

OS Windows XP SP3
CPU Core 2 Duo T7250 2GHz
Memory 2.5G
HDD 5400rpm

table作成

EmployeeOrderの属性EmployeeIDはテーブルEmployeeの属性EmployeeIDへの外部キー

create table Employee(EmployeeID int primary key) 
create table EmployeeOrder(
	OrderID int primary key,
	EmployeeID int not null constraint fkOrderCust references Employee(EmployeeID)) 

data挿入

Employee:300万行
EmployeeOrder:100万行、外部キーの値はランダムに設定。

;with
Range( I, L )
AS
(
	SELECT 1, 3000000
),
EID( ID )
AS
(
	SELECT I
	FROM Range
	UNION ALL
	SELECT ID+1
	FROM EID
	WHERE ID < ( SELECT L FROM Range )
)
INSERT INTO Employee
SELECT ID
FROM EID
OPTION ( MAXRECURSION 0);

;with
Range( I, L )
AS
(
	SELECT 1, 1000000
),
OID( ID )
AS
(
	SELECT I
	FROM Range
	UNION ALL
	SELECT ID+1
	FROM OID
	WHERE ID < ( SELECT L FROM Range )
)
INSERT INTO EmployeeOrder
SELECT ID, ABS(CHECKSUM(NEWID())) % 3000000 + 1
FROM OID
OPTION ( MAXRECURSION 0);

実行するSQL

Employeeに存在するEmployeeIDを持つEmployeeOrderを取得

Select * from EmployeeOrder eo 
where exists 
( 
select * From Employee e 
where eo.EmployeeID = e.EmployeeID 
) 

外部キー有効版結果

キャッシュ無し
(1000000 行処理されました)
テーブル 'EmployeeOrder'。スキャン回数 1、論理読み取り数 2110、物理読み取り数 6、先行読み取り数 2091、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。

SQL Server 実行時間: 
、CPU 時間 = 453 ミリ秒、経過時間 = 45583 ミリ秒。
キャッシュ有り
(1000000 行処理されました)
テーブル 'EmployeeOrder'。スキャン回数 1、論理読み取り数 2110、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。

 SQL Server 実行時間: 
、CPU 時間 = 485 ミリ秒、経過時間 = 44385 ミリ秒。
実行プラン

f:id:eo-oe-aaaa:20120607120143j:image

外部キー無効版結果

外部キー制約を無効にして実行する。

ALTER TABLE EmployeeOrder
NOCHECK CONSTRAINT fkOrderCust
キャッシュ無し
(1000000 行処理されました)
テーブル 'EmployeeOrder'。スキャン回数 3、論理読み取り数 2136、物理読み取り数 15、先行読み取り数 2107、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'Employee'。スキャン回数 3、論理読み取り数 4889、物理読み取り数 43、先行読み取り数 4846、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'Worktable'。スキャン回数 0、論理読み取り数 0、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。

 SQL Server 実行時間: 
、CPU 時間 = 3595 ミリ秒、経過時間 = 51534 ミリ秒。
キャッシュ有り
(1000000 行処理されました)
テーブル 'EmployeeOrder'。スキャン回数 3、論理読み取り数 2136、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'Employee'。スキャン回数 3、論理読み取り数 4893、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'Worktable'。スキャン回数 0、論理読み取り数 0、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。

 SQL Server 実行時間: 
、CPU 時間 = 2907 ミリ秒、経過時間 = 46566 ミリ秒。
実行プラン

f:id:eo-oe-aaaa:20120607123255j:image

外部キー無効版結果(並列実行しない)

MAXDOPオプションを使用して並列実行を禁止する。

Select * from EmployeeOrder eo 
where exists 
( 
select * From Employee e 
where eo.EmployeeID = e.EmployeeID 
)
OPTION( MAXDOP 1)
キャッシュ無し
(1000000 行処理されました)
テーブル 'Worktable'。スキャン回数 0、論理読み取り数 0、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'Employee'。スキャン回数 1、論理読み取り数 4843、物理読み取り数 13、先行読み取り数 4830、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'EmployeeOrder'。スキャン回数 1、論理読み取り数 2110、物理読み取り数 6、先行読み取り数 2091、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。

 SQL Server 実行時間: 
、CPU 時間 = 3234 ミリ秒、経過時間 = 49076 ミリ秒。
キャッシュ有り
(1000000 行処理されました)
テーブル 'Worktable'。スキャン回数 0、論理読み取り数 0、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'Employee'。スキャン回数 1、論理読み取り数 4843、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。
テーブル 'EmployeeOrder'。スキャン回数 1、論理読み取り数 2110、物理読み取り数 0、先行読み取り数 0、LOB 論理読み取り数 0、LOB 物理読み取り数 0、LOB 先行読み取り数 0。

 SQL Server 実行時間: 
、CPU 時間 = 3000 ミリ秒、経過時間 = 47496 ミリ秒。
実行プラン

f:id:eo-oe-aaaa:20120607124154j:image

結論

外部キー制約があることでEmployeeテーブルとの結合条件が確実にTrueとなることが分かる。
その為、Employeeテーブルとの結合をする必要がなくなる。
よって、SELECT時のパフォーマンスが上がる。
この結果ではSELECT結果返すところが時間かかってて全体ではそんなに時間は変わらないけど。SELECT COUNT(*)とかにしたほうが結果わかりやすい。

SQLServer SPのプリフィックス

SPの名前を決めるとき、
分かりやすさから「sp_〜」と名づけることが多いと思うが、
これをやるとパフォーマンスが若干低下してしまう。

なぜならば、
「sp_〜」という名前は、システムが内部的に使用するSPのプリフィックスになっている。
その為、ユーザDBのSPを実行する処理の前に、
必ずmasterDBに対して該当SPが存在するかのチェックを行ってしまう。

参考URL:
http://www.sqlmag.com/Article/ArticleID/23011/sql_server_23011.html

SQL Serverの一時テーブルを削除し忘れた場合の挙動

#1つの一時テーブルの場合、
ローカル一時テーブルとなり、
セッションが切断されるまで有効となる。
つまり、ConnectionオブジェクトのCloseメソッドを呼び出すまで有効となる。
その為、一時テーブルを削除し忘れたとしてもClose時に、
自動的に削除される。

もし、同一セッション内で同じ処理を複数回動かすと
削除されていない一時テーブルを作成しようとするので、
エラーとなる。




SqlConnection
http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqlconnection(VS.71).aspx

一時テーブルの挙動
http://www.microsoft.com/japan/sql/prodinfo/compare/fororacle/sqlserver_02_chap1_08.mspx

GridViewデータ0件時、ヘッダーを表示する(4.0未満版)

DataGridではデータ0件でもヘッダーが表示されていたが、
GridViewではヘッダーが表示されなくなる。

4.0未満の場合は、簡単に設定する方法が存在しないので、
手動でヘッダーを出力するコードを書く必要あり。
例えば以下のようなコード。

if (dtFunding.Rows.Count != 0)
{
    grdFunding.DataSource = dtFunding;
    grdFunding.DataBind();
}
else
{
     // ダミー行を追加して、その行を非表示とする。
     dtFunding.Rows.Add(dtFunding.NewRow());
     grdFunding.DataSource = dtFunding;
     grdFunding.DataBind();
     grdFunding.Rows[0].Visible = false;
}
http://stackoverflow.com/questions/354369/gridview-show-headers-on-empty-data-source