Pięć lat spóźnienia na przyjęcie.
Jest o tym mowa w podanych linkach akceptowanej odpowiedzi, ale myślę, że zasługuje na jednoznaczną odpowiedź na SO - dynamiczne budowanie zapytania w oparciu o podane parametry. Na przykład:
Ustawiać
-- 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
Procedura
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
Stosowanie
exec spDoSearch @TopCount = 3
exec spDoSearch @FirstName = 'Dick'
Plusy:
- łatwe do napisania i zrozumienia
- elastyczność - łatwe generowanie zapytania o trudniejsze filtrowanie (np. dynamiczne TOP)
Cons:
- możliwe problemy z wydajnością w zależności od podanych parametrów, indeksów i ilości danych
Nie bezpośrednia odpowiedź, ale związana z problemem, czyli dużym obrazem
Zazwyczaj te przechowywane procedury filtrowania nie są zmiennoprzecinkowe, ale są wywoływane z jakiejś warstwy usług. Pozostawia to opcję przeniesienia logiki biznesowej (filtrowania) z SQL do warstwy usługi.
Jednym z przykładów jest użycie LINQ2SQL do wygenerowania zapytania w oparciu o dostarczone filtry:
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;
}
Plusy:
- dynamicznie generowane zapytanie w oparciu o dostarczone filtry. Nie ma potrzeby wyszukiwania parametrów ani wskazówek dotyczących ponownej kompilacji
- nieco łatwiej pisać dla osób ze świata OOP
- zazwyczaj sprzyja wydajności, ponieważ będą wydawane „proste” zapytania (jednak potrzebne są odpowiednie indeksy)
Cons:
- Można osiągnąć ograniczenia LINQ2QL i wymuszać przejście na LINQ2Objects lub powrót do czystego rozwiązania SQL, w zależności od przypadku
- nieostrożne pisanie LINQ może generować okropne zapytania (lub wiele zapytań, jeśli załadowane zostaną właściwości nawigacji)