192

テーブルを検索するためのストアドプロシージャを作成しています。私は多くの異なる検索フィールドを持っていますが、それらはすべてオプションです。これを処理するストアドプロシージャを作成する方法はありますか?ID、FirstName、LastName、Titleの4つのフィールドを持つテーブルがあるとします。私はこのようなことをすることができます:

CREATE PROCEDURE spDoSearch
    @FirstName varchar(25) = null,
    @LastName varchar(25) = null,
    @Title varchar(25) = null
AS
    BEGIN
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            FirstName = ISNULL(@FirstName, FirstName) AND
            LastName = ISNULL(@LastName, LastName) AND
            Title = ISNULL(@Title, Title)
    END

この種の作品。ただし、FirstName、LastName、またはTitleがNULLであるレコードは無視されます。検索パラメータでTitleが指定されていない場合、TitleがNULLであるレコードを含めたいと思います。FirstNameとLastNameで同じです。おそらく動的SQLでこれを実行できることはわかっていますが、それは避けたいと思います。

4

6 に答える 6

271

与えられたパラメータに基づいて検索を動的に変更することは複雑な問題であり、非常にわずかな違いであっても、ある方法で別の方法で行うと、パフォーマンスに大きな影響を与える可能性があります。重要なのは、インデックスを使用すること、コンパクトなコードを無視すること、コードの繰り返しについて心配することを無視することです。適切なクエリ実行プランを作成する必要があります (インデックスを使用します)。

これを読んで、すべての方法を検討してください。最適な方法は、パラメーター、データ、スキーマ、および実際の使用法によって異なります。

Erland Sommarskog による T-SQL の動的検索条件

Erland Sommarskog による動的 SQL の呪いと祝福

適切な SQL Server 2008 バージョン (SQL 2008 SP1 CU5 (10.0.2746) 以降) を使用している場合は、この小さなトリックを使用して実際にインデックスを使用できます。

Erland の記事 を参照OPTION (RECOMPILE)してクエリに 追加すると、ローカル変数のランタイム値に基づいてクエリ プランが作成される前にSQL Server が内部から解決し、インデックスを使用できるようになります。OR(@LastName IS NULL OR LastName= @LastName)

これはどの SQL Server バージョンでも機能しますが (適切な結果を返します)、SQL 2008 SP1 CU5 (10.0.2746) 以降を使用している場合にのみ OPTION(RECOMPILE) を含めます。OPTION(RECOMPILE) はクエリを再コンパイルします。リストされているバージョンのみがローカル変数の現在の実行時の値に基づいて再コンパイルされ、最高のパフォーマンスが得られます。そのバージョンの SQL Server 2008 でない場合は、その行をオフのままにしてください。

CREATE PROCEDURE spDoSearch
    @FirstName varchar(25) = null,
    @LastName varchar(25) = null,
    @Title varchar(25) = null
AS
    BEGIN
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
                (@FirstName IS NULL OR (FirstName = @FirstName))
            AND (@LastName  IS NULL OR (LastName  = @LastName ))
            AND (@Title     IS NULL OR (Title     = @Title    ))
        OPTION (RECOMPILE) ---<<<<use if on for SQL 2008 SP1 CU5 (10.0.2746) and later
    END
于 2010-08-05T14:21:36.850 に答える
28

@KM からの回答は、これまでのところ良いものですが、彼の初期のアドバイスの 1 つを完全にフォローアップすることはできません。

..., コンパクトなコードを無視する, コードの繰り返しについて心配することを無視する, ...

最高のパフォーマンスを達成しようとしている場合は、オプションの条件の可能な組み合わせごとにカスタム クエリを作成する必要があります。これは極端に聞こえるかもしれません。オプションの基準がたくさんある場合はそうかもしれませんが、パフォーマンスは多くの場合、努力と結果の間のトレードオフです。実際には、特注のクエリで対象とすることができるパラメータの組み合わせの一般的なセットがあり、次に他のすべての組み合わせに対する一般的なクエリ (他の回答による) がある場合があります。

CREATE PROCEDURE spDoSearch
    @FirstName varchar(25) = null,
    @LastName varchar(25) = null,
    @Title varchar(25) = null
AS
BEGIN

    IF (@FirstName IS NOT NULL AND @LastName IS NULL AND @Title IS NULL)
        -- Search by first name only
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            FirstName = @FirstName

    ELSE IF (@FirstName IS NULL AND @LastName IS NOT NULL AND @Title IS NULL)
        -- Search by last name only
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            LastName = @LastName

    ELSE IF (@FirstName IS NULL AND @LastName IS NULL AND @Title IS NOT NULL)
        -- Search by title only
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            Title = @Title

    ELSE IF (@FirstName IS NOT NULL AND @LastName IS NOT NULL AND @Title IS NULL)
        -- Search by first and last name
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            FirstName = @FirstName
            AND LastName = @LastName

    ELSE
        -- Search by any other combination
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
                (@FirstName IS NULL OR (FirstName = @FirstName))
            AND (@LastName  IS NULL OR (LastName  = @LastName ))
            AND (@Title     IS NULL OR (Title     = @Title    ))

END

このアプローチの利点は、オーダーメイドのクエリによって処理される一般的なケースでは、クエリが可能な限り効率的であることです。提供されていない基準による影響はありません。また、考えられるすべての状況を満たそうとするのではなく、特定のオーダーメイドのクエリをターゲットにして、インデックスやその他のパフォーマンスの向上を図ることができます。

于 2015-03-25T19:19:26.190 に答える
27

次の場合に行うことができます。

CREATE PROCEDURE spDoSearch
   @FirstName varchar(25) = null,
   @LastName varchar(25) = null,
   @Title varchar(25) = null
AS
  BEGIN
      SELECT ID, FirstName, LastName, Title
      FROM tblUsers
      WHERE
        (@FirstName IS NULL OR FirstName = @FirstName) AND
        (@LastNameName IS NULL OR LastName = @LastName) AND
        (@Title IS NULL OR Title = @Title)
END

ただし、データによっては、動的クエリを作成して実行する方がよい場合があります。

于 2010-08-05T14:35:20.953 に答える
12

パーティーに5年遅れ。

受け入れられた回答の提供されたリンクで言及されていますが、SOに関する明示的な回答に値すると思います-提供されたパラメーターに基づいてクエリを動的に構築します。例えば:

設定

-- drop table Person
create table Person
(
    PersonId INT NOT NULL IDENTITY(1, 1) CONSTRAINT PK_Person PRIMARY KEY,
    FirstName NVARCHAR(64) NOT NULL,
    LastName NVARCHAR(64) NOT NULL,
    Title NVARCHAR(64) NULL
)
GO

INSERT INTO Person (FirstName, LastName, Title)
VALUES ('Dick', 'Ormsby', 'Mr'), ('Serena', 'Kroeger', 'Ms'), 
    ('Marina', 'Losoya', 'Mrs'), ('Shakita', 'Grate', 'Ms'), 
    ('Bethann', 'Zellner', 'Ms'), ('Dexter', 'Shaw', 'Mr'),
    ('Zona', 'Halligan', 'Ms'), ('Fiona', 'Cassity', 'Ms'),
    ('Sherron', 'Janowski', 'Ms'), ('Melinda', 'Cormier', 'Ms')
GO

手順

ALTER PROCEDURE spDoSearch
    @FirstName varchar(64) = null,
    @LastName varchar(64) = null,
    @Title varchar(64) = null,
    @TopCount INT = 100
AS
BEGIN
    DECLARE @SQL NVARCHAR(4000) = '
        SELECT TOP ' + CAST(@TopCount AS VARCHAR) + ' *
        FROM Person
        WHERE 1 = 1'

    PRINT @SQL

    IF (@FirstName IS NOT NULL) SET @SQL = @SQL + ' AND FirstName = @FirstName'
    IF (@LastName IS NOT NULL) SET @SQL = @SQL + ' AND FirstName = @LastName'
    IF (@Title IS NOT NULL) SET @SQL = @SQL + ' AND Title = @Title'

    EXEC sp_executesql @SQL, N'@TopCount INT, @FirstName varchar(25), @LastName varchar(25), @Title varchar(64)', 
         @TopCount, @FirstName, @LastName, @Title
END
GO

使用法

exec spDoSearch @TopCount = 3
exec spDoSearch @FirstName = 'Dick'

長所:

  • 書きやすく、理解しやすい
  • 柔軟性 - 複雑なフィルタリング (動的 TOP など) のクエリを簡単に生成

短所:

  • 指定されたパラメータ、インデックス、およびデータ量によっては、パフォーマンスの問題が発生する可能性があります

直接的な答えではありませんが、問題、つまり全体像に関連しています

通常、これらのフィルタリング ストアド プロシージャは浮遊するのではなく、何らかのサービス レイヤーから呼び出されます。これにより、ビジネス ロジック (フィルタリング) を SQL からサービス レイヤーに移動するオプションが残されます。

一例として、LINQ2SQL を使用して、提供されたフィルターに基づいてクエリを生成します。

    public IList<SomeServiceModel> GetServiceModels(CustomFilter filters)
    {
        var query = DataAccess.SomeRepository.AllNoTracking;

        // partial and insensitive search 
        if (!string.IsNullOrWhiteSpace(filters.SomeName))
            query = query.Where(item => item.SomeName.IndexOf(filters.SomeName, StringComparison.OrdinalIgnoreCase) != -1);
        // filter by multiple selection
        if ((filters.CreatedByList?.Count ?? 0) > 0)
            query = query.Where(item => filters.CreatedByList.Contains(item.CreatedById));
        if (filters.EnabledOnly)
            query = query.Where(item => item.IsEnabled);

        var modelList = query.ToList();
        var serviceModelList = MappingService.MapEx<SomeDataModel, SomeServiceModel>(modelList);
        return serviceModelList;
    }

長所:

  • 提供されたフィルターに基づいて動的に生成されたクエリ。パラメータのスニッフィング再コンパイルのヒントは不要
  • OOP の世界にいる人にとっては、いくらか書きやすくなります。
  • 「単純な」クエリが発行されるため、通常はパフォーマンスに適しています(ただし、適切なインデックスが必要です)

短所:

  • 場合によっては、LINQ2QL の制限に達し、強制的に LINQ2Objects にダウングレードするか、純粋な SQL ソリューションに戻る可能性があります。
  • LINQ を不注意に記述すると、ひどいクエリ (ナビゲーション プロパティが読み込まれている場合は多くのクエリ) が生成される可能性があります。
于 2016-12-19T15:40:36.247 に答える
8

条件を拡張しますWHERE:

WHERE
    (FirstName = ISNULL(@FirstName, FirstName)
    OR COALESCE(@FirstName, FirstName, '') = '')
AND (LastName = ISNULL(@LastName, LastName)
    OR COALESCE(@LastName, LastName, '') = '')
AND (Title = ISNULL(@Title, Title)
    OR COALESCE(@Title, Title, '') = '')

つまり、さまざまなケースをブール条件と組み合わせます。

于 2010-08-05T14:36:24.960 に答える
-3

これも機能します:

    ...
    WHERE
        (FirstName IS NULL OR FirstName = ISNULL(@FirstName, FirstName)) AND
        (LastName IS NULL OR LastName = ISNULL(@LastName, LastName)) AND
        (Title IS NULL OR Title = ISNULL(@Title, Title))
于 2014-07-17T16:10:32.163 に答える