2010-08-05 35 views
147

我正在创建一个存储过程来通过表进行搜索。我有许多不同的搜索字段,所有这些都是可选的。有没有办法创建一个存储过程来处理这个问题?假设我有一个包含四个字段的表格:ID,FirstName,LastName和Title。我可以这样做:如何在T-SQL存储过程中使用可选参数?

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为NULL的记录 - 对于FirstName和LastName是相同的。我知道我可以用动态SQL来做到这一点,但我想避免这种情况。

+0

看一看这里: http://stackoverflow.com/questions/11396919/building-dynamic-where-clause在存储过程/ 25473624#25473624 – 2014-09-03 12:24:54

+1

请尝试以下语句: 'code' ISNULL(FirstName,')= ISNULL(@FirstName,'') - 这将使每个NULL 为空字符串,可以通过等式进行比较。运营商。 如果你想获得所有标题如果输入参数为空,然后尝试这样的事情: 'code'FirstName = @FirstName或@FirstName IS NULL。 – baHI 2016-01-13 10:58:18

回答

214

根据给定的参数动态更改搜索是一个复杂的主题,并且以一种方式进行处理,即使只有非常微小的差异,也会产生巨大的性能影响。关键是要使用索引,忽略紧凑的代码,不要担心重复的代码,你必须制定一个好的查询执行计划(使用索引)。

阅读并考虑所有的方法。你最好的方法将取决于您的参数,你的数据,你的模式,你的实际使用情况:

Dynamic Search Conditions in T-SQL by by Erland Sommarskog

The Curse and Blessings of Dynamic SQL by Erland Sommarskog

如果你有正确的SQL Server 2008的版本(SQL 2008 SP1 CU5(10.0 0.2746)及更高版本),你可以用这个小动作来实际使用索引:

添加OPTION (RECOMPILE)到您的查询,see Erland's article,和SQL Server将解决从(@LastName IS NULL OR LastName= @LastName)OR前查询计划基于本地变量的运行时值创建,并且可以使用索引。

这将适用于任何SQL Server版本(返回正确的结果),但只有在SQL 2008 SP1 CU5(10.0.2746)及更高版本中才包括OPTION(RECOMPILE)。 OPTION(RECOMPILE)将重新编译你的查询,只有列出的verison会根据本地变量的当前运行时间值重新编译它,这会给你提供最好的性能。如果不在该版本的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 
+12

小心AND/OR优先。 AND优先于OR,所以如果没有正确的括号,这个例子不会产生预期的结果...所以它读取:(@FirstName IS NULL OR(FirstName = @FirstName))AND(@LastNameIS NULL OR(LastName = @LastName))AND(@TitleIS NULL OR(Title = @Title)) – Bliek 2011-10-10 11:51:04

+0

@Bliek,谢谢我修复它。 – 2011-10-10 15:05:36

+3

您的回答很好。感谢所有的信息! – 2013-05-22 06:03:40

18

您可以在以下情况下做的,

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 

但是依赖于数据有时更好地创造动态查询和执行它们。

6

扩展您的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, '') = '') 

我。即将不同的情况与布尔条件结合起来。

-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)) 
19

从@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 

这种方法的优点是,在通过定制处理的常见情况查询的查询是有效的,因为它可以 - 有在未供给标准没有影响。此外,索引和其他性能增强可针对特定的定制查询,而不是试图满足所有可能的情况。

2

晚了五年。

在提供的接受答案的链接中提到了它,但我认为它应该在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; 
        } 
    

    优点。没有parameter sniffingrecompile提示需要

  • 比较容易写那些在OOP世界
  • 通常表现友好,因为“简单”的查询将发行(适当的索引仍需要虽然)

缺点:

  • LINQ2QL限制可能达到并强制降级到LINQ2Objects或根据情况要回纯SQL解决方案
  • LINQ的粗心写作可能会产生可怕的查询(或许多查询,如果导航属性加载)
相关问题