2010-06-29 107 views
2

我正在研究一個搜索查詢(帶有一個asp.net 3.5前端),它看起來很簡單,但相當複雜。 完整的查詢是:編寫一個可怕的SQL搜索查詢(第二階段)

set ANSI_NULLS ON 
set QUOTED_IDENTIFIER ON 
go 

ALTER PROCEDURE [dbo].[usp_Item_Search] 
    @Item_Num varchar(30) = NULL 
    ,@Search_Type int = NULL 
    ,@Vendor_Num varchar(10) = NULL 
    ,@Search_User_ID int = 0 
    ,@StartDate smalldatetime = NULL 
    ,@EndDate smalldatetime = NULL 
AS 
DECLARE @SQLstr as nvarchar(4000) 

Set @SQLstr = 'SELECT RecID, Vendor_Num, Vendor_Name, InvoiceNum, Item_Num, 
(SELECT CONVERT(VARCHAR(11), RecDate, 106) AS [DD MON YYYY]) As RecDate, NeedsUpdate, RecAddUserID FROM [tbl_ItemLog] where 1=1 ' 

IF (@Item_Num IS NOT NULL and LTRIM(@Item_Num) <> '') 
    Begin 
     If @Search_Type = 0 
      BEGIN 
       Set @SQLstr = @SQLstr + 'AND Item_Num LIKE ''' + @Item_Num + '%''' 
      END 
     If @Search_Type = 1 
      BEGIN 
       Set @SQLstr = @SQLstr + 'AND Item_Num LIKE ''%' + @Item_Num + '%''' 
      END 
     If @Search_Type = 2 
      BEGIN 
       Set @SQLstr = @SQLstr + 'AND Item_Num LIKE ''%' + @Item_Num + '''' 
      END 
    End 

IF (@Vendor_Num IS NOT NULL and LTRIM(@Vendor_Num) <> '') 
    Begin 
     Set @SQLstr = @SQLstr + ' AND Vendor_Num = ''' + @Vendor_Num + '''' 
    End 

IF (@Search_User_ID IS NOT NULL and @Search_User_ID > 0) 
    Begin 
     Set @SQLstr = @SQLstr + ' AND RecAddUserID = ' + convert(nvarchar(20),@Search_User_ID) 
    End 

Set @SQLstr = @SQLstr + ' AND (RecDate BETWEEN ''' + convert(nvarchar(10),@StartDate,106) + ''' AND ''' + convert(nvarchar(10),@EndDate,106) + ''')' 

PRINT (@SQLstr) 
--Execute (@SQLstr) 

當我通過所有空的參數值,我得到一個錯誤:

"Failed to convert parameter value from a String to a Int32."

正在調用存儲過程的asp.net代碼:

 //Display search results in GridView; 
     SqlConnection con = new SqlConnection(strConn); 
     //string sqlItemSearch = "usp_Item_Search"; 
     SqlCommand cmdItemSearch = new SqlCommand(sqlItemSearch, con); 
     cmdItemSearch.CommandType = CommandType.StoredProcedure; 

     cmdItemSearch.Parameters.Add(new SqlParameter("@Item_Num", SqlDbType.VarChar, 30)); 
     cmdItemSearch.Parameters["@Item_Num"].Value = txtItemNumber.Text.Trim(); 

     cmdItemSearch.Parameters.Add(new SqlParameter("@Search_Type", SqlDbType.Int)); 
     cmdItemSearch.Parameters["@Search_Type"].Value = ddlSearchType.SelectedItem.Value; 

     cmdItemSearch.Parameters.Add(new SqlParameter("@Vendor_Num", SqlDbType.VarChar, 10)); 
     cmdItemSearch.Parameters["@Vendor_Num"].Value = txtVendorNumber.Text.Trim(); 

     cmdItemSearch.Parameters.Add(new SqlParameter("@Search_User_ID", SqlDbType.Int)); 
     cmdItemSearch.Parameters["@Search_User_ID"].Value = ddlSeachUser.SelectedItem.Value; 

     if (!string.IsNullOrEmpty(txtStartDate.Text)) 
     { 
      cmdItemSearch.Parameters.Add(new SqlParameter("@StartDate", SqlDbType.DateTime)); 
      cmdItemSearch.Parameters["@StartDate"].Value = Convert.ToDateTime(txtStartDate.Text.Trim()); 
     } 
     else 
     { 
      cmdItemSearch.Parameters.Add(new SqlParameter("@StartDate", SqlDbType.DateTime)); 
      cmdItemSearch.Parameters["@StartDate"].Value = Convert.ToDateTime("01/01/1996"); 
     } 

     if (!string.IsNullOrEmpty(txtEndDate.Text)) 
     { 
      cmdItemSearch.Parameters.Add(new SqlParameter("@EndDate", SqlDbType.DateTime)); 
      cmdItemSearch.Parameters["@EndDate"].Value = Convert.ToDateTime(txtEndDate.Text.Trim()); 
     } 
     else 
     { 
      cmdItemSearch.Parameters.Add(new SqlParameter("@EndDate", SqlDbType.DateTime)); 
      cmdItemSearch.Parameters["@EndDate"].Value = Convert.ToDateTime(DateTime.Now); 
     } 
     con.Open(); 

     SqlDataAdapter ada = new SqlDataAdapter(cmdItemSearch); 
     DataSet ds = new DataSet(); 
     ada.Fill(ds); 

      gvSearchDetailResults.DataSource = ds; 
      gvSearchDetailResults.DataBind(); 
      pnlSearchResults.Visible = true; 

我該如何解決這個問題?

+1

看着你的ASP代碼...你確定下拉列表框的值是整數嗎?它可能是抓取顯示值而不是索引?正如我想的那樣,SQL Server不會在錯誤消息中給出Int32 - 這是一個.NET錯誤。 – 2010-06-29 19:28:58

+0

好的。下拉列表的第一個值是「請選擇一個沒有索引值爲0的值」。現在我收到另一條錯誤消息:「將字符串轉換爲smalldatetime數據類型時轉換失敗。」 – DotNetRookie 2010-06-29 19:34:30

回答

3

你不太建設字符串正確,據我可以告訴。如果沒有傳入@Item_Num,那麼最終將沒有WHERE關鍵字......您將只有「FROM [tblItem_Log] AND ...」

我會使所有條件附加爲「而且......」,併爲您最初的聲明中使用:

FROM [tbl_Item_Log] WHERE (1=1) 

既然你的代碼返回生成的字符串,爲什麼不將其寫入SSMS並嘗試運行呢?

我也只是注意到,如果你不通過日期值,你會最終執行一個NULL字符串,因爲你最終的連接將最終導致NULL。如果您打算使用動態SQL來構建查詢,那麼這些是您需要非常關注的事情。

一旦我糾正了我能夠運行存儲過程沒有任何錯誤(至少生成看起來像一個有效的SQL語句)。這導致我相信它可能是基礎表中數據類型的問題。你能提供這個定義嗎?

最後一個音符:就個人而言,我會使用

CONVERT(VARCHAR(11), RecDate, 106) AS RecDate 

,而不是你有看似不必要的子查詢。

另一個編輯: 您可能想要刪除檢查LTRIM(@Search_User_ID)代碼<>'。這是一個毫無意義的代碼,可能是一個特定於您的服務器/連接的設置導致它由於類型不匹配而失敗。

+0

我需要一個假期。我沒有看到where子句問題。就返回的記錄數而言,1 = 1是否會混淆查詢? – DotNetRookie 2010-06-29 18:14:45

+4

@DotNetRookie:'WHERE 1 = 1'將被優化,並且是一個很好的方法來指定一個WHERE子句,這個子句很容易附加到動態SQL等情況。 – 2010-06-29 18:18:13

+0

這很好。但我仍然得到那個錯誤消息。 「無法將參數值從字符串轉換爲Int32。」 – DotNetRookie 2010-06-29 18:22:15

1
IF (Search_User_ID IS NOT NULL) 

需要一個@符號盈變量

你說你正在傳遞空字符串的所有變量,但一個是int型的,它不能把未int類型數據的空字符串。不敢相信我沒有注意到,那是第一次。

+0

好的。完成:-) – DotNetRookie 2010-06-29 18:06:52

+0

你在談論哪個int變量? – DotNetRookie 2010-06-29 19:04:00

+1

@Search_User_ID 您需要通過在NUll中發送非空字符串''來測試它沒有值。 – HLGEM 2010-06-29 19:16:41

0

你爲什麼不使用單一的參數化查詢是這樣的:

select 
    recdid, 
    Vendor_Num, 
    Vendor_Name, 
    InvoiceNum, 
    Item_Num, 
    CONVERT(VARCHAR(11), RecDate, 106) as RecDate, 
    NeedsUpdate, 
    RecAddUserID 
FROM 
    [tbl_ItemLog] as t 
where 
    (((Item_num like @Item_Num + '%' and @Search_Type = 0) OR 
    (Item_num like '%' + @Item_Num + '%' and @Search_Type = 1) OR 
    (Item_num like '%' + @Item_Num + '%' and @Search_Type = 2)) 
     OR 
    @Item_Num IS NULL) AND 
    (Vendor_Num = @Vendor_Num OR @Vendor_Num IS NULL) AND 
    (RecAddUserId = @Search_User_Id OR @Search_User_Id IS NULL) AND 
    (RecDate BETWEEN @StartDate AND @EndDate) 
+1

只是說明這是更乾淨,但實際上更慢(性能明智)。 – JonH 2010-06-29 18:12:15

+0

在我將其插入我的應用程序之前,我需要研究一下您的回覆。感謝您的建議。 – DotNetRookie 2010-06-29 18:15:57

+0

與動態SQL的性能相比,這是一個向後*步*。像這樣的查詢將確保幾乎每次都難以解析,除非參數完全相同。還有參數嗅探的風險... – 2010-06-29 18:28:53

0

這裏確實有幾個不同的存儲過程。爲什麼不把它們分開寫呢?所有由switch語句控制的內容都可以輕易放在程序代碼中。 LTRIM呼叫也一樣。

你可以使用switch語句從一個SP中調用它們;但我認爲最好不要將它們合併在一起。 SP查詢將更容易優化,代碼將被簡化。鞏固他們並沒有太多的收穫。

的不知道您的業務規則,但你可以簡化與

switch(search_type) {  
case 1: 
    do_query_type_1(args); 
    break; 
case 2: 
    do_query_type_2(args); 
    break; 
case 3: 
    do_query_type_3(args); 
    break; 
default: 
    whatever ... 
} 

此之外還有SQL看起來你對其中項目數量提供或不提供單獨的情況下的邏輯。其他領域也一樣。你的每個用例看起來像解析爲一個非常簡單的查詢。

+0

我真的不知道「三種不同的存儲過程」。 – DotNetRookie 2010-06-29 18:35:27

+0

重寫爲「幾個」。 – dkretz 2010-06-29 18:37:03

+0

我不明白這是「幾個」不同的存儲過程。你能有點特別嗎? – DotNetRookie 2010-06-29 18:39:26