2010-08-05 55 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的粗心寫作可能會產生可怕的查詢(或許多查詢,如果導航屬性加載)
相關問題