ストアドプロシージャを使用したデータへのアクセス
-
05-07-2019 - |
質問
「ベストプラクティス」の1つストアドプロシージャを介してデータにアクセスしています。なぜこのシナリオが良いのか理解しています。 私の動機は、データベースとアプリケーションのロジックを分割することです(ストアドプロシージャの動作が同じ場合、テーブルを変更できます)、SQLインジェクションの防御(ユーザーは" select * from some_tables"を実行できず、ストアドプロシージャのみを呼び出すことができます)、セキュリティ
私が知らないのは、動的フィルターを使用してデータにアクセスする方法です。
MSSQL 2005を使用しています。
テーブルがある場合:
CREATE TABLE tblProduct (
ProductID uniqueidentifier -- PK
, IDProductType uniqueidentifier -- FK to another table
, ProductName nvarchar(255) -- name of product
, ProductCode nvarchar(50) -- code of product for quick search
, Weight decimal(18,4)
, Volume decimal(18,4)
)
次に、4つのストアドプロシージャを作成する必要があります(作成/読み取り/更新/削除)。
" create"のストアドプロシージャ簡単です。
CREATE PROC Insert_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
INSERT INTO tblProduct ( ProductID, IDProductType, ... etc .. ) VALUES ( @ProductID, @IDProductType, ... etc ... )
END
" delete"のストアドプロシージャ簡単です。
CREATE PROC Delete_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ...
END
" update"のストアドプロシージャ" delete"と似ていますが、これが正しい方法であるかどうかはわかりません。すべての列を更新するのは効率的ではないと思います。
CREATE PROC Update_Product( @ProductID uniqueidentifier, @Original_ProductID uniqueidentifier, @IDProductType uniqueidentifier, @Original_IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
UPDATE tblProduct SET ProductID = @ProductID, IDProductType = @IDProductType, ... etc ...
WHERE ProductID = @Original_ProductID AND IDProductType = @Original_IDProductType AND ... etc ...
END
最後の-「読み取り」用のストアドプロシージャ私にとってはちょっとした謎です。複雑な条件のフィルター値を渡す方法は?いくつか提案があります:
XMLパラメータを使用してwhere条件を渡す:
CREATE PROC Read_Product ( @WhereCondition XML ) AS BEGIN
DECLARE @SELECT nvarchar(4000)
SET @SELECT = 'SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct'
DECLARE @WHERE nvarchar(4000)
SET @WHERE = dbo.CreateSqlWherecondition( @WhereCondition ) --dbo.CreateSqlWherecondition is some function which returns text with WHERE condition from passed XML
DECLARE @LEN_SELECT int
SET @LEN_SELECT = LEN( @SELECT )
DECLARE @LEN_WHERE int
SET @LEN_WHERE = LEN( @WHERE )
DECLARE @LEN_TOTAL int
SET @LEN_TOTAL = @LEN_SELECT + @LEN_WHERE
IF @LEN_TOTAL > 4000 BEGIN
-- RAISE SOME CONCRETE ERROR, BECAUSE DYNAMIC SQL ACCEPTS MAX 4000 chars
END
DECLARE @SQL nvarchar(4000)
SET @SQL = @SELECT + @WHERE
EXEC sp_execsql @SQL
END
しかし、「4000」の制限は、 1つのクエリの文字がいです。
次の提案は、すべての列にフィルターテーブルを使用することです。フィルター値をフィルターテーブルに挿入し、フィルターのIDでストアドプロシージャを呼び出します。
CREATE TABLE tblFilter (
PKID uniqueidentifier -- PK
, IDFilter uniqueidentifier -- identification of filter
, FilterType tinyint -- 0 = ignore, 1 = equals, 2 = not equals, 3 = greater than, etc ...
, BitValue bit , TinyIntValue tinyint , SmallIntValue smallint, IntValue int
, BigIntValue bigint, DecimalValue decimal(19,4), NVarCharValue nvarchar(4000)
, GuidValue uniqueidentifier, etc ... )
CREATE TABLE Read_Product ( @Filter_ProductID uniqueidentifier, @Filter_IDProductType uniqueidentifier, @Filter_ProductName uniqueidentifier, ... etc ... ) AS BEGIN
SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume
FROM tblProduct
WHERE ( @Filter_ProductID IS NULL
OR ( ( ProductID IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 1 ) AND NOT ( ProductID IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 2 ) )
AND ( @Filter_IDProductType IS NULL
OR ( ( IDProductType IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 1 ) AND NOT ( IDProductType IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 2 ) )
AND ( @Filter_ProductName IS NULL OR ( ... etc ... ) )
END
しかし、この提案は少し複雑だと思います。
「ベストプラクティス」がありますか?このタイプのストアドプロシージャを実行するにはどうしますか?
解決
最初:削除ルーチンの場合、where句には主キーのみを含める必要があります。
2番目:更新ルーチンでは、コードを実行する前に最適化を試みないでください。実際、アプリケーションのプロファイルを作成してボトルネックがどこにあるかを確認できるまで、最適化を試みないでください。 1行の1列の更新と1行のすべての列の更新の速度がほぼ同じであることを確実に伝えることができます。 DBMSで時間がかかるのは、(1)データを書き込むディスクブロックを見つけ、(2)書き込みの一貫性を保つために他のライターをロックアウトすることです。最後に、変更が必要な列のみを更新するために必要なコードを記述することは、一般に実行が難しく、保守が難しくなります。本当にうるさくなりたい場合は、すべての列を更新する場合と比較して、どの列が変更されたかを判断する速度を比較する必要があります。それらをすべて更新する場合、それらのいずれかを読む必要はありません。
3番目:各検索パスに対して1つのストアドプロシージャを作成する傾向があります。あなたの例では、主キーごとに1つ、外部キーごとに1つ作成し、アプリケーションで必要に応じて新しいアクセスパスごとに1つ追加します。機敏に。必要のないコードを書かないでください。ストアドプロシージャの代わりにビューを使用することにも同意しますが、ストアドプロシージャを使用して複数の結果セットを返すことができます(MSSQLの一部のバージョンで)または行を列に変更することができます。
たとえば、主キーで7行を取得する必要がある場合、いくつかのオプションがあります。主キーで1行を7回取得するストアドプロシージャを呼び出すことができます。すべての呼び出し間で接続を開いたままにしておくと、これで十分に高速になる場合があります。一度に特定の数(10個など)を超えるIDを必要としないことがわかっている場合は、" and ID in(arg1、arg2、arg3 ...)"のようなwhere句を含むストアドプロシージャを記述できます。未使用の引数がNULLに設定されていることを確認してください。動的SQLを生成する必要があると判断した場合、TSQLは他の言語と同様に間違いを犯しやすいため、ストアドプロシージャは気にしません。また、データベースを使用して文字列操作を行うことによるメリットはありません。ほとんど常にボトルネックであるため、必要以上の作業をDBに与えることには意味がありません。
他のヒント
データの読み取りには、セキュリティのためにストアドプロシージャを必要としないか、ロジックを分離するためにビューを使用できます。
ビューの選択のみを許可します。
表示するレコードを制限したり、フィールド名を変更したり、多くのテーブルを1つの論理的な「テーブル」に結合したりできます。
Insert / Update / Selectストアドプロシージャの作成が「ベストプラクティス」であることに同意しません。アプリケーション全体がSPで記述されていない限り、アプリケーションでデータベースレイヤーを使用してこれらのCRUDアクティビティを処理します。さらに良いことに、ORMテクノロジーを使用してそれらを処理します。
私の提案は、現在または今後必要なことをすべて実行するストアドプロシージャを作成しようとしないことです。テーブルの主キーに基づいて行を取得する必要がある場合は、そのためのストアドプロシージャを作成します。一連の基準を満たすすべての行を検索する必要がある場合は、その基準が何であるかを見つけ、それを実行するストアドプロシージャを作成します。
特定の問題ではなく、考えられるすべての問題を解決するソフトウェアを作成しようとすると、通常は有用なものを提供できません。
selectストアドプロシージャは、次のように実行できます。必要なストアドプロシージャは1つだけですが、where句にはさまざまなアイテムが必要です。パラメーターのいずれかまたは組み合わせを渡すと、一致するすべてのアイテムが取得されます。したがって、必要なストアドプロシージャは1つだけです。
Create sp_ProductSelect
(
@ProductID int = null,
@IDProductType int = null,
@ProductName varchar(50) = null,
@ProductCode varchar(10) = null,
...
@Volume int = null
)
AS
SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct'
Where
((@ProductID is null) or (ProductID = @ProductID)) AND
((@ProductName is null) or (ProductName = @ProductName)) AND
...
((@Volume is null) or (Volume= @Volume))
SQL 2005では、nvarchar(max)をサポートしています。これには2Gの制限がありますが、通常のnvarcharですべての文字列操作を事実上受け入れます。これが最初のアプローチで必要なものに適合するかどうかをテストすることができます。