14

検索条件をプログラムで SQL ストアド プロシージャに追加するにはどうすればよいですか? 私のアプリケーション (C#) では、ストアド プロシージャ (SQL Server 2008R2) を使用しています。

ALTER PROCEDURE [dbo].[PROC001]
@userID varchar(20),
@password varchar(20)
AS
SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password

このクエリをさらに条件で拡張したいのですが、プログラムの実行により、このクエリを使用する条件の数がわかりません.2、3、6、または20.これらの条件をプログラムで次のように追加します。

SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password
AND Field2 = '1' AND Field3 = '0' OR Field4 <> '8' AND Field5 < '100' ....

条件をストアド プロシージャに動的に送信することは可能ですか?

4

3 に答える 3

19

次のように、SQL でのみこれを行うことができます。

SELECT * 
FROM tUsers 
WHERE 1 = 1
  AND (@userID IS NULL OR RTRIM(Name) = @userID )
  AND (@password IS NULL OR RTRIM(Password) = @password)
  AND (@field2 IS NULL OR Field2 = @field2)
....

値を指定してストアド プロシージャにパラメータを渡すと、NULL条件全体が無視されます。

WHERE 1 = 1:クエリにパラメータが渡されない場合にクエリを機能させるために追加しました。この場合、1 = 1常にtrueであるため、すべての結果セットが返されます。

于 2012-07-04T13:16:10.857 に答える
7

編集 - 可能であれば、LINQ ベースの ORM の設定

ADO でこれを行う必要がない場合、より良い解決策は、最終的にパラメーター化されたアドホック SQL を構築する ORM を使用することです。これは両方の長所です。動的クエリの柔軟性が得られ、オプティマイザーを混乱させる冗長なフィルターがなく、クエリ プラン自体がキャッシュ可能であり、インジェクション攻撃などの厄介な問題から安全です。また、Linq ベースの ORM クエリを使用すると、読みやすくなります。

 // Build up a non-materialized IQueryable<>
 var usersQuery = db.Users;
 if (!string.IsNullOrEmpty(userID))
 {
       usersQuery = usersQuery.Where(u => u.Name == userId);
 }
 // Of course, you wouldn't dream of storing passwords in cleartext.
 if (!string.IsNullOrEmpty(anotherField))
 {
       usersQuery = usersQuery.Where(u => u.AnotherColumn == anotherField);
 }
 ...
 // Materialize (and execute) the query
 var filteredUsers = usersQuery.ToList();

複雑なクエリについては、 PredicateBuilderを確認することをお勧めします

ADO / 手動クエリ作成

sp_executesql以下のように、SQL を動的に構築するために使用できます。変数をパラメータ化すれば、SQL インジェクションや引用符のエスケープなどの問題から安全に処理されます。

CREATE PROCEDURE [dbo].[PROC001]
    @userID varchar(20),
    @pwdHash varchar(20),
    @optionalParam1 NVARCHAR(50) = NULL -- Other optional parameters
AS        
    BEGIN        
        SET NOCOUNT ON        

        DECLARE @SQL NVARCHAR(MAX)        

        -- Mandatory / Static part of the Query here. 
        -- Cleartext passwords are verboten, and RTRIM is redundant in filters
        SET @SQL = N'SELECT * FROM tUsers WHERE Name = @userID AND PwdHash = @pwdHash'

        IF @OptionalParam1 IS NOT NULL        
            BEGIN        
                SET @SQL = @SQL + N' AND AnotherField = @OptionalParam1'    
            END        

        EXEC sp_executesql @SQL,        
            N'@userID varchar(20),
            @pwdHash varchar(20),
            @optionalParam1 NVARCHAR(50)'
            ,@userID = @userID
            ,@pwdHash = @pwdHash
            ,@optionalParam1 = @optionalParam1
    END

Re、なぜWHERE (@x IS NULL OR @x = Column)悪い考えなのですか?

(以下、私のコメントより)

「オプション パラメータ」パターンは、小さなテーブルで使用される場合、オプション フィルタの多数の順列をクエリするための「スイス アーミー ナイフ」としてうまく機能しますが、残念ながら、大きなテーブルでは、フィルタのすべての順列に対して 1 つのクエリ プランになります。これにより、パラメーター スニッフィングの問題により、オプションのパラメーターの特定の順列でクエリのパフォーマンスが低下する可能性があります。可能であれば、冗長なフィルターを完全に削除する必要があります。

Re: 述語で関数を適用するのはなぜ悪い考えなのですか?

例えば

WHERE SomeFunction(Column) = @someParameter

述部で関数を使用すると、RDBMS によるインデックスの使用が不適格となることがよくあります ( "non-sargable" )。

この場合、RTRIMSQL サーバーは比較中に末尾のスペースを無視するため、 は不要です。

于 2012-07-04T13:29:44.333 に答える
2

プロシージャを文字列として使用し、条件付きの文字列を連結して実行することができます。

ALTER PROCEDURE [dbo].[PROC001] @userID varchar(20), @password varchar(20), @WhereToAdd varchar(MAX) AS 

exec ('SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password AND ' + @WhereToAdd)
于 2012-07-04T13:18:14.170 に答える