4

「ベスト プラクティス」の 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 / read / update / delete ) を作成する必要があります。

「作成」のストアド プロシージャは簡単です。

CREATE PROC Insert_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
   INSERT INTO tblProduct ( ProductID, IDProductType, ... etc .. ) VALUES ( @ProductID, @IDProductType, ... etc ... )
END

「削除」のストアドプロシージャも簡単です。

CREATE PROC Delete_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
    DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ...
END

「更新」のストアド プロシージャは「削除」のストアド プロシージャと似ていますが、これが正しい方法であるかどうかはわかりません。すべての列を更新するのは効率的ではないと思います。

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

そして、最後の「読み取り」のストアド プロシージャは、私にとっては少し謎です。複雑な条件のフィルター値を渡す方法 いくつかの提案があります:

where条件を渡すためのXMLパラメータの使用:

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

しかし、1回のクエリで「4000」文字という制限は見苦しいと思います。

次の提案は、すべての列にフィルター テーブルを使用することです。フィルター値をフィルター テーブルに挿入し、フィルターの 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

しかし、この提案は少し複雑だと思います。

このタイプのストアド プロシージャを実行するための「ベスト プラクティス」はありますか?

4

6 に答える 6

6

データを読み取る場合、セキュリティのためにストアド プロシージャを使用したり、ロジックを分離したりする必要はありません。ビューを使用できます。

ビューでのみ選択を許可します。

表示されるレコードを制限したり、フィールド名を変更したり、多くのテーブルを 1 つの論理的な「テーブル」に結合したりできます。

于 2008-10-29T15:39:33.797 に答える
5

まず、削除ルーチンの場合、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 句を含むストアド プロシージャを記述して、make することができます。未使用の引数が NULL に設定されていることを確認してください。動的 SQL を生成する必要があると判断した場合、TSQL は他の言語と同じように間違いやすいため、ストアド プロシージャは使用しません。また、データベースを使用して文字列操作を行っても何のメリットもありません。ほとんどの場合、これがボトルネックになるため、DB に必要以上の作業を与えても意味がありません。

于 2008-10-29T15:40:53.293 に答える
3

挿入/更新/選択ストアドプロシージャの作成が「ベストプラクティス」であることに同意しません。アプリケーション全体がSPで記述されている場合を除き、アプリケーションでデータベースレイヤーを使用して、これらのCRUDアクティビティを処理します。さらに良いことに、ORMテクノロジーを使用してそれらを処理します。

于 2008-10-29T17:47:54.427 に答える
2

select ストアド プロシージャを次のように実行して、where 句に 1 つのストアド プロシージャのみを必要とし、任意の数の異なるアイテムを必要とすることができます。パラメータのいずれかまたは組み合わせを渡すと、一致するすべてのアイテムが取得されるため、必要なストアド プロシージャは 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))
于 2008-10-29T16:18:06.277 に答える
2

私が提案するのは、現在または今後必要になる可能性のあるすべてのことを行うストアド プロシージャを作成しようとしないことです。テーブルの主キーに基づいて行を取得する必要がある場合は、それを行うためのストアド プロシージャを記述します。一連の基準を満たすすべての行を検索する必要がある場合は、その基準が何であるかを調べ、それを行うためのストアド プロシージャを記述します。

特定の問題セットではなく、考えられるすべての問題を解決するソフトウェアを作成しようとすると、通常、有用なものを提供することに失敗します。

于 2008-10-29T16:04:52.310 に答える
0

SQL 2005では、2Gの制限があるnvarchar(max)をサポートしますが、通常のnvarcharですべての文字列操作を事実上受け入れます。これが最初のアプローチで必要なものに適合するかどうかをテストすることをお勧めします。

于 2008-10-29T15:54:35.607 に答える