ラベル SQL の投稿を表示しています。 すべての投稿を表示
ラベル SQL の投稿を表示しています。 すべての投稿を表示

2016年3月24日木曜日

SQL Serverのクエリから、アクセスしているユーザー名を取得する関数の違い

SQL Serverのユーザー名を取得する関数は多岐に渡る。そのため以下のクエリで、ログインユーザーとメソッドの関係について調査した。

SELECT SYSTEM_USER, USER, SESSION_USER, USER_NAME(), SUSER_NAME(), SUSER_SNAME (), ORIGINAL_LOGIN ()


関数名 Windows認証 SQL Server認証
SYSTEM_USER Windows認証アカウント
(ドメイン\ユーザー名 形式)
SQL Server認証
アカウント名
USERスキーマ名(dboなど) SQL Server認証
アカウント名
SESSION_USER スキーマ名(dboなど) SQL Server認証
アカウント名
USER_NAME() スキーマ名(dboなど) SQL Server認証
アカウント名
SUSER_NAME Windows認証アカウント
(ドメイン\ユーザー名 形式)
SQL Server認証
アカウント名
SUSER_SNAME () Windows認証アカウント
(ドメイン\ユーザー名 形式)
SQL Server認証
アカウント名
ORIGINAL_LOGIN () Windows認証アカウント
(ドメイン\ユーザー名 形式)
SQL Server認証
アカウント名

2014年11月6日木曜日

SQL Server Unitテストフレームワーク tSQLt 【サンプルテスト】

前回説明した環境構築・テストデータの作成を行ったtSQLtの環境を使ったUnitTestのサンプル集です。最後にテストの実行方法とテストケースの削除等の後片付けについて記載しています。


-- テストケース1(期待値との単純比較)
-- テストケースはストアドプロシージャで書く
-- プロシージャの名前は日本語の名称でもエラーにならないが、テスト実行されなくなってしまう。
CREATE PROCEDURE [testGroup1].[TestCase1]
AS
BEGIN
 --テスト実行1回目
 DECLARE @n int
 EXEC dbo.TargetProcedure @n OUTPUT
 --1回目の結果比較
    EXEC tSQLt.AssertEquals 30, @n
 --テスト実行2回目
 EXEC dbo.TargetProcedure @n OUTPUT -- 1回目の結果を反映した2回目のテスト
 --2回目の結果比較
    EXEC tSQLt.AssertEquals 40, @n
END;
GO



-- テストケース2(FakeTableの使用例)
CREATE PROCEDURE [testGroup1].[TestCase2]
AS
BEGIN
 --実在するテーブルを偽テーブル(FakeTable)に置き換える。
 EXEC tSQLt.FakeTable 'dbo.TestTable';
 INSERT INTO dbo.TestTable(Id) VALUES(9) --外部キーの制約、NOT NULL制約を無視してデータの挿入が可能
 --テスト実行
 DECLARE @n int
 EXEC dbo.TargetProcedure @n OUTPUT
 --結果比較
    EXEC tSQLt.AssertEquals 9, @n
END;
GO



-- テストケース3(テーブル型の比較例)
CREATE PROCEDURE [testGroup1].[TestCase3]
AS
BEGIN
 --テスト実行
 CREATE TABLE #Actual (Name varchar(20),  Id int)
 INSERT INTO #Actual EXEC dbo.TargetProcedure2
 --期待値作成
 SELECT  TOP(0) * INTO #Expected FROM #Actual
 INSERT INTO #Expected VALUES ('abc',30)
 --結果比較
 EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual';
END;
GO



-- テストケース4(ストアドプロシージャ内のストアドプロシージャへの引数確認)
CREATE PROCEDURE [testGroup1].[TestCase4]
AS
BEGIN
 --ログを取得するストアドプロシージャを指定
    EXEC tSQLt.SpyProcedure 'dbo.TargetProcedure';
 --テスト実行
 EXEC dbo.TargetProcedure2
 SELECT *
  INTO #Actual
  FROM dbo.TargetProcedure_SpyProcedureLog;

--結果比較
 SELECT  TOP(0) * INTO #Expected FROM #Actual
 INSERT INTO #Expected VALUES (Null,'TargetProcedure2内で実行') --OUTPUTパラメータの値はNullになる。
 EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual';
END;
GO


-- テストケース5(ストアドプロシージャ内のストアドプロシージャのOUTPUTパラメータの書き換え)
CREATE PROCEDURE [testGroup1].[TestCase5]
AS
BEGIN
 --OUTPUTパラメータを書き換えるストアドプロシージャを指定
    EXEC tSQLt.SpyProcedure 'dbo.TargetProcedure','SET @maxId = 1000';

 --テスト実行
 CREATE TABLE #Actual (Name varchar(20),  Id int)
 INSERT INTO #Actual EXEC dbo.TargetProcedure2

 --結果比較
 SELECT  TOP(0) * INTO #Expected FROM #Actual
 INSERT INTO #Expected VALUES ('abc',1000)
 EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual';
END;
GO



-- テストケース6(ストアドプロシージャ内のストアドプロシージャを書き換える。)
CREATE PROCEDURE [testGroup1].[TestCase6]
AS
BEGIN

--OUTPUTパラメータを書き換えるストアドプロシージャを指定
    EXEC tSQLt.SpyProcedure 'dbo.TargetProcedure','SET @maxId = 1000';
    EXEC tSQLt.RemoveObject 'dbo.TargetProcedure';
    EXEC ('CREATE PROCEDURE TargetProcedure  @maxId INT OUTPUT, @dummy varchar(100) = NULL AS BEGIN SELECT @maxId = 2000 END;');

 --テスト実行
 CREATE TABLE #Actual (Name varchar(20),  Id int)
 INSERT INTO #Actual EXEC dbo.TargetProcedure2

--結果比較
 SELECT  TOP(0) * INTO #Expected FROM #Actual
 INSERT INTO #Expected VALUES ('abc',2000)
 EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual';
END;
GO




--テスト実行
tSQLt.Run [testGroup1]
go
-- テスト時に追加したデータは残らない。偽テーブルのデータも残らない。
select * from TestTable
select * from NumTable
go

--すべてのTestClassのテストを実行
tSQLt.RunAll


--削除
EXEC tSQLt.DropClass [testGroup1]
Drop Procedure TargetProcedure
Drop Procedure TargetProcedure2
Drop Procedure [testGroup1].[TestCase1]
Drop Procedure [testGroup1].[TestCase2]
Drop Procedure [testGroup1].[TestCase3]
Drop Procedure [testGroup1].[TestCase4]
Drop Procedure [testGroup1].[TestCase5]
Drop Procedure [testGroup1].[TestCase6]
Drop table TestTable
Drop table NumTable

--FakeTableを単体で実行すると元データを退避したテーブルが作られる。
--このテーブルに外部キーがある場合、参照されているテーブルが削除できなくなってしまうので
--以下のクエリで参照しているテーブルを探して探して消す。
--select OBJECT_NAME(parent_object_id),* from sys.foreign_keys
--FakeTableの名前変換情報は以下のテーブルに持っているようだが、ここまでは削除しない方が良さそう。
--select * from tSQLt.Private_RenamedObjectLog

SQL Server Unitテストフレームワーク tSQLt 【環境構築】

tSQLtはSQL Serverの単体テスト(Unit Test)を実施するための、オープンソーステストフレームワークです。

環境構築は簡単で以下のURLから資源をダウンロードして以下のスクリプトを実行するだけです。

--環境構築
SetClrEnabled.sql
開いているDBのTRUSTWORTHYオプションをONにする。
これによりCLRの実行が許可される。
テストするDB毎に実行する。

Example.sql
サンプルDB(tSQLt_Example)、tSQLt、テストケースが復元される。
tSQLt.RunAllと実行するだけで、サンプルテストケースを実行可能

tSQLt.class.sql
tSQLt本体、テストを実行する場合は、「SetClrEnabled.sql」とこのスクリプトを実行する。




--tSQLt.Run で実行できる最小単位の名前(スキーマ)を定義
-- 日本語名も指定可能だが全角文字は結果表示がずれる。
EXEC tSQLt.NewTestClass 'testGroup1';
GO

----テストデータ作成
CREATE TABLE NumTable (Num int PRIMARY KEY, str varchar(100))
INSERT INTO NumTable VALUES(1,'一')
INSERT INTO NumTable VALUES(2,'二')
INSERT INTO NumTable VALUES(3,'三')

CREATE TABLE TestTable (Id int, Num int NOT NULL,PRIMARY KEY(Id),FOREIGN KEY (Num) REFERENCES NumTable (Num))
INSERT INTO TestTable VALUES(10,3)
INSERT INTO TestTable VALUES(20,2)
INSERT INTO TestTable VALUES(30,1)
GO


--テスト対象のストアドプロシージャ1
CREATE PROCEDURE TargetProcedure
 @maxId INT OUTPUT,
 @dummy varchar(100) = NULL
AS
BEGIN
 SELECT @maxId = MAX(Id) FROM TestTable
 INSERT INTO NumTable VALUES(@maxId+100,'TargetProcedureで追加') -- テーブルにデータ追加
 INSERT INTO TestTable VALUES(@maxId+10, @maxId+100) -- テーブルにデータ追加
END
GO

--テスト対象のストアドプロシージャ2
CREATE PROCEDURE TargetProcedure2
AS
BEGIN
 DECLARE @Id int
 EXEC dbo.TargetProcedure @Id OUTPUT,'TargetProcedure2内で実行'
 SELECT 'abc',@Id
 RETURN
END
GO


サンプルテストはこちらに記載しています。
  1 期待値との単純比較
  2 FakeTableの使用例
  3 テーブル型の比較例
  4 ストアドプロシージャ内のストアドプロシージャへの引数確認
  5 ストアドプロシージャ内のストアドプロシージャのOUTPUTパラメータの書き換え
  6 ストアドプロシージャ内のストアドプロシージャを書き換える。

tSQLt
http://tsqlt.org/user-guide/quick-start/

日本語解説
http://www.infoq.com/jp/news/2012/02/tSQLt

連番のカラムを作る


--連番のカラムを作る
CREATE TABLE #T(name varchar(100))

INSERT INTO #T VALUES ('ABC'),('DEF'),('GHI')

SELECT t1.name, count(*) AS seq
 FROM #T t1
  INNER JOIN #T t2 ON t1.name >= t2.name
 GROUP BY t1.name
 ORDER BY seq DESC
DROP TABLE #T

NULLがあると「NOT IN」と「NOT EXISTS」は変換できない。


NULLがあると「NOT IN」と「NOT EXISTS」は変換できない。(「IN」と「EXISTS」は変換可能)
CREATE TABLE #T2(name varchar(100))
CREATE TABLE #T3(name varchar(100))
INSERT INTO #T2 VALUES ('ABC'),('DEF'),('GHI')
INSERT INTO #T3 VALUES ('ABC'),('DEF'),(NULL)


SELECT *
 FROM #T2 t2
 WHERE EXISTS (SELECT * FROM #T3 WHERE t2.name = #T3.name)

SELECT *
 FROM #T2 t2
 WHERE t2.name IN (SELECT * FROM #T3)


SELECT *
 FROM #T2 t2
 WHERE NOT EXISTS (SELECT * FROM #T3 WHERE t2.name = #T3.name)
--NOT EXISTSと結果が違う。(NULLが#T2にある場合も「NOT IN」と「NOT EXISTS」で結果が異なる)
SELECT *
 FROM #T2 t2
 WHERE t2.name NOT IN (SELECT * FROM #T3)


DROP TABLE #T2
DROP TABLE #T3

使用できる統計情報

[INSERT、DELETE および UPDATE ステートメントの数] 
クエリの結果として実行された INSERT、DELETE、または UPDATE の各ステートメントの数。 


[INSERT、DELETE、UPDATE ステートメントが処理した行数] 
クエリの実行の一環として実行された INSERT、DELETE、または UPDATE の各ステートメントの影響を受けた行の数。 


[SELECT ステートメントの数] 
クエリの実行の一環として接続を通じて実行された SELECT ステートメントの数。  この数には、カーソルから行を取得するための FETCH ステートメントも含まれます。 


[SELECT ステートメントから返された行数] 
クエリの実行の一環として選択された行の数。  この数には、Transact-SQL ステートメントによって生成されたすべての行が反映されています。これには、呼び出し元によって実際に消費されなかった行も含まれます (たとえば、実行が取り消された場合など)。  この数には、カーソルから行を取得するための FETCH ステートメントも含まれます。 


[トランザクション数] 
クエリの実行の一環として開始されたユーザー トランザクションの数。ロールバックも含まれます。 


[サーバーのラウンド トリップ数] 
クエリの実行の一環として、接続がサーバーにコマンドを送信し、応答を受信した回数。 


[クライアントから送信した TDS パケット] 
クエリの実行中にクライアントからデータベース サーバーに送信された TDS パケットの数。  大量の処理を伴うコマンドでは、複数のバッファーが必要になります。  たとえば、大量の処理を伴うコマンドがサーバーに送信され、6 個のパケットを必要とする場合、サーバーのラウンド トリップ数は 1 ずつ増加し、クライアントから送信された TDS パケットの数は 6 ずつ増加します。 


[サーバーから受信した TDS パケット] 
クライアントが SQL Server のインスタンスから受信した TDS パケットの数。 


[クライアントから送信されたバイト数]  クエリの実行中にクライアントが SQL Server のインスタンスに送信したバイトの数。 


[サーバーから受信したバイト数]  クエリの実行中にクライアントが SQL Server のインスタンスから受信したバイトの数。 


[クライアントの処理時間] 
クエリの実行中にクライアントがコードの実行に要した累積時間。 


[総実行時間] 
クエリの実行中にクライアントが処理に要した累積時間をミリ秒単位で表したもの。これには、クライアントがサーバーからの応答の待機に要した時間と、コードの実行に要した時間が含まれます。 


[サーバー応答の待機時間]  クライアントがサーバーからの応答を待機するのに要した累積時間をミリ秒単位で表したもの。



統計のリセット方法

[データ] メニューの [T-SQL エディタ] をポイントし、[クライアント統計のリセット] をクリックします。
T-SQL エディタ内を右クリックし、ショートカット メニューの [クライアント統計のリセット] をクリックします。


クエリ結果の概要
http://msdn.microsoft.com/ja-jp/library/aa833205(v=vs.100).aspx

2014年3月18日火曜日

SQLインジェクション

インジェクション調べる
http://npnl.hatenablog.jp/entry/20080412/1207965105

自己流のSQLインジェクション対策は危険
http://blog.tokumaru.org/2013/02/security-measures-of-own-way-are-unsafe.html
SQLインジェクション対策の「究極形」に従うことを強く推奨します。それは下記のとおりです。
文字列連結(sprintf等を含む)を用いてSQL文を組み立てない
静的プレースホルダを用いてSQL文にパラメータを割り当てる
データベース接続時に文字エンコーディングを指定する

Like 句に対する SQL インジェクション対策(ASP.NET,C#, VB.NET 編)
http://www.websec-room.com/2013/12/15/1307
SQL インジェクションは、プレースホルダを利用することで対策が可能ですが、Like 句のワイルドカードついては、ほとんどの情報元が「別途エスケープが必要です」としか記載されていません。
 %   0個以上の文字
 _    1文字
 []   []内に指定した任意の文字
ESCAPE 句を使用することにより、% _ [ を簡単にエスケープできるようになります。
WHERE field LIKE @Parameter ESCAPE 'エスケープ記号として使用する文字'
このエスケープの仕様はSQL Server,Oracle,DB2等、メジャーなDBではみんな同じらしい。

2013年10月24日木曜日

SQL Server の最大容量仕様


インデックス キーごとの列数7 16
外部キーごとの列数 16
主キーごとの列数 16
SELECT ステートメントごとの列数 4096
INSERT ステートメントごとの列数 4096


SQL ステートメントが含まれた文字列の長さ (バッチ サイズ)
65,536 * ネットワーク パケット サイズ
ネットワーク パケット サイズとは、アプリケーションとリレーショナル データベース エンジンの間の通信に
使用される表形式のデータ ストリーム (TDS) パケットのサイズです。
既定のパケット サイズは 4 KB であり、network packet size 構成オプションによって制御されます。

SQL Server の最大容量仕様
http://msdn.microsoft.com/ja-jp/library/ms143432.aspx