П’ять років спізнюється до партії.
Він згадується в наданих посиланнях прийнятої відповіді, але я думаю, що він заслуговує на явну відповідь на 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 може генерувати жахливі запити (або багато запитів, якщо навігаційні властивості завантажені)