2017-07-30 122 views
0

我有一個SQL Server數據庫,其中包含客戶數據的Person表。在我的Web應用程序中,彈出式搜索框(jqGrid)允許用戶指定在搜索中使用的字段/過濾器以及操作和數據。所以這些字段就像名字,上次訪問日期等(映射到Person表,雖然不是所有的字段都與列名匹配),並且操作是包含,開始於,結束於,大於等等(每一個都被傳遞以「cn」,「bw」,「ew」,「gt」)這樣的雙字母代碼表示。總共有15個不同的領域可以被過濾和12個操作。SQL Server在where子句中包含搜索篩選器列表

(有表中的約50列,所以我只是這裏包括幾個):

CREATE TABLE [dbo].[Person] 
(
    [PersonId] [int] IDENTITY(1,1) NOT NULL, 
    [PersonTypeId] [int] NULL, 
    [Firstname] [nvarchar](200) NULL, 
    [Middlename] [nvarchar](200) NULL, 
    [Lastname] [nvarchar](200) NULL, 
    [Name] [nvarchar](200) NULL, 
) 

INSERT INTO [dbo].[Person]([PersonTypeId],[Firstname],[Middlename],[Lastname],[Name]) 
VALUES (1, 'James', 'Joseph', 'Martin', ''), 
     (1, 'Jim', 'Joseph', 'Martyn', ''), 
     (1, 'James', 'John', 'Martine', ''), 
     (2, 'James', 'Sean', 'Martin', 'Martin & Co'), 
     (2, 'John', 'Joseph', 'Martin', 'Martin & Martin') 

我添加了一個表值參數的存儲過程來傳遞效果很好的搜索條件,所以現在在搜索存儲過程中,我有一個udt包含用戶輸入的所有字段/操作/數據。

CREATE TYPE [dbo].[udt_Filter] AS TABLE 
     (
      [Field] [nvarchar](50), 
      [Op] [nvarchar](50), 
      [Data] [nvarchar](50) 
     ) 

通過UDT傳遞會是這樣的過濾器:

Field Op Data 
'Name', 'bw', 'Mart' 
'LastVisit', 'gt', '01.07.2017' 

現在,我不知道什麼是包含在搜索過濾器的最佳途徑。我知道我可以使用動態SQL進行工作,但如果可能的話,我寧願使用連接來完成它,但是我看不到任何將Person名稱過濾器與Person表中的數據結合在一起的方式。

ALTER PROCEDURE [dbo].[pr_GetPersons] 
    (
     @Filters dbo.udt_Filter READONLY, 
     @Operation nvarchar(3) -- AND or OR 
    ) 
AS 
BEGIN 
    SELECT [PersonTypeId], [Firstname], [Middlename], [Lastname], [Name] 
    FROM [dbo].[Person] 
    WHERE PersonTypeId IN (1, 2, 3) 
     -- @Operation can I somehow join to @Filters and filter the Select result using each row in @Filters?? 
END 

所有過濾條件都與AND或OR結合,它們作爲@Operation傳遞到存儲過程。

+0

過濾器是否總是與'和',''或'或用戶指定的選項組合?一些建議的背景閱讀是[這裏](http://www.sommarskog.se/dyn-search-2008.html)。 – HABO

+0

Hi @HABO - 我沒有提到所有的陳述都是用AND或OR結合的,加上最初的WHERE PersonTypeId IN子句 –

+0

你如何處理子表達式,例如: 'A和(B或C)而不是(D或(E而不是F))'?目前還不清楚你在'Field' /'Op' /'Data'中的表現方式,更不用說'和'/'或'了。 – HABO

回答

2

不可能通過Person表和@Filters表參數之間的連接來解決此動態過濾。如果你沒有動態SQL,有15個字段和12個操作符,你最終會得到一個怪物where子句。我懷疑存儲過程在這種情況下會表現得很好。

相反,我建議去動態SQL。這裏有一個存儲過程可以用作起點:

CREATE PROCEDURE [dbo].[pr_GetPersons] 
    (@Filters dbo.udt_Filter READONLY) 
AS 
BEGIN 

    DECLARE 
     @SelectList nvarchar(max), 
     @WhereClause nvarchar(max), 
     @SqlCommand nvarchar(max) 

    SET @SelectList = ISNULL(STUFF((SELECT ', ' + QUOTENAME(Field) AS [text()] FROM @Filters FOR XML PATH('')), 1, 1, ''), '*') 

    SET @WhereClause = STUFF((
     SELECT 
      ' AND ' + 
      -- Translate opcodes into SQL Server expressions. 
      CASE Op 
       WHEN 'cn' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME('%' + Data + '%', '''') 
       WHEN 'bw' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME(Data + '%', '''') 
       WHEN 'ew' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME('%' + Data, '''') 
       WHEN 'gt' THEN QUOTENAME(Field) + '>' + QUOTENAME(Data, '''') 
       WHEN 'lt' THEN QUOTENAME(Field) + '<' + QUOTENAME(Data, '''') 
      END AS [text()] 
     FROM 
      @Filters 
     FOR XML PATH(''), type).value('.', 'nvarchar(max)'), 
     1, 5, '') 

    SET @SqlCommand = 'SELECT ' + @SelectList + ' FROM dbo.Person WHERE PersonTypeId IN (1, 2, 3)' + ISNULL(NULLIF(' AND ', @WhereClause) + @WhereClause, '') 

    EXEC(@SqlCommand) 

END 

它只返回過濾器中的列。如果您希望它返回表格中的所有列,而不管過濾器中的內容如何,​​請將@SelectList變量設置爲*

所有過濾條件都使用AND運算符連接。 (在@Fields變量沒有跡象顯示如何將它們結合起來。所以我想和運營商所需要的。)

CASE表達式解釋來自@Filters表操作碼生成where子句可以移動到自己的功能。 QUOTENAME(Data, '''')用於在這種情況下形成正確的字符串,當Data是一個字符串並且包含單引號時。

該過程不檢查Person表中是否存在過濾器字段。應特別注意濾波器值(Data),尤其是日期/時間和小數類型,因爲沒有類型轉換(所有濾波器值在形成where子句時用作字符串),也不檢查過濾器值類型是否與列匹配類型在Person表中。

希望它有幫助。