테이블을 통해 검색하기 위해 저장 프로 시저를 만들고 있습니다. 다양한 검색 필드가 있으며 모두 선택 사항입니다. 이를 처리 할 저장 프로 시저를 만드는 방법이 있습니까? ID, FirstName, LastName 및 Title의 네 가지 필드가있는 테이블이 있다고 가정하겠습니다. 나는 이런 식으로 할 수 있습니다 :
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
이런 종류의 작품. 그러나 이름, 성 또는 제목이 NULL 인 레코드는 무시합니다. 제목이 검색 매개 변수에 지정되어 있지 않으면 제목이 NULL 인 레코드를 포함하고 싶습니다. 이름과성에 동일합니다. 동적 SQL 로이 작업을 수행 할 수는 있지만 피하고 싶습니다.
답변
주어진 매개 변수를 기반으로 검색을 동적으로 변경하는 것은 복잡한 주제이며 약간의 차이 만 있어도 다른 방식으로 검색하면 성능에 큰 영향을 줄 수 있습니다. 핵심은 인덱스를 사용하고, 컴팩트 코드를 무시하고, 코드 반복에 대한 걱정을 무시하는 것입니다. 좋은 쿼리 실행 계획을 만들어야합니다 (인덱스 사용).
이것을 읽고 모든 방법을 고려하십시오. 가장 좋은 방법은 매개 변수, 데이터, 스키마 및 실제 사용량에 따라 다릅니다.
Erland Sommarskog의 T-SQL의 동적 검색 조건
Erland Sommarskog의 동적 SQL의 저주와 축복
적절한 SQL Server 2008 버전 (SQL 2008 SP1 CU5 (10.0.2746) 이상)이있는 경우이 작은 트릭을 사용하여 실제로 인덱스를 사용할 수 있습니다.
추가 OPTION (RECOMPILE)
, 쿼리에 ERLAND의 문서를 참조 하고, 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
답변
@KM의 답변은 가능한 한 좋은 것이지만 그의 초기 조언 중 하나를 완전히 따르지 않습니다.
…, 컴팩트 코드를 무시하고, 코드 반복에 대한 걱정을 무시하고 …
최상의 성능을 얻으려면 선택 가능한 기준의 각 가능한 조합에 대해 맞춤형 쿼리를 작성해야합니다. 이것은 극단적으로 들릴 수도 있고, 선택적인 기준이 많으면 그럴 수도 있지만 성과는 노력과 결과 사이의 절충점 인 경우가 많습니다. 실제로는 맞춤형 쿼리로 타겟팅 할 수있는 일반적인 매개 변수 조합 세트가있을 수 있으며 다른 모든 조합에 대해 일반 쿼리 (다른 답변에 따라)가있을 수 있습니다.
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
이 접근 방식의 장점은 맞춤형 쿼리로 처리되는 일반적인 경우 쿼리가 가능한 한 효율적이라는 것입니다. 제공되지 않은 기준에 의해 영향을받지 않습니다. 또한 인덱스 및 기타 성능 향상은 가능한 모든 상황을 만족시키기보다는 특정 맞춤형 쿼리를 대상으로 할 수 있습니다.
답변
다음과 같은 경우에 할 수 있습니다
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
그러나 때로는 데이터에 의존하여 동적 쿼리를 생성하고 실행하는 것이 좋습니다.
답변
파티에 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 제한에 도달하고 경우에 따라 LINQ2Object로 다운 그레이드하거나 강제 SQL 솔루션으로 되돌아 갈 수 있음
- 부주의 한 LINQ 작성은 끔찍한 쿼리 (또는 탐색 속성이로드 된 경우 많은 쿼리)를 생성 할 수 있습니다.
답변
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, '') = '')
즉, 다른 경우를 부울 조건과 결합합니다.
답변
이것은 또한 작동합니다 :
...
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))