Пять лет опоздал на вечеринку.
Это упомянуто в предоставленных ссылках принятого ответа, но я думаю, что оно заслуживает явного ответа на 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;
}
Плюсы:
- динамически генерируемый запрос на основе предоставленных фильтров. Нет необходимости анализировать параметры или перекомпилировать подсказки
- несколько легче написать для тех, кто в мире ООП
- как правило, дружественные к производительности, так как будут выдаваться «простые» запросы (все же необходимы соответствующие индексы)
Минусы:
- Могут быть достигнуты ограничения LINQ2QL, что приведет к понижению до объектов LINQ2Objects или возврату к чисто SQL-решению в зависимости от ситуации.
- неосторожное написание LINQ может привести к ужасным запросам (или множеству запросов, если загружены свойства навигации)