编写一个可怕的SQL搜索查询(第二阶段)

时间:2010-06-29 18:02:27

标签: sql sql-server sql-server-2005 tsql dynamic-sql

我正在开发一个搜索查询(带有asp.net 3.5前端),这看起来很简单,但非常复杂。 完整的查询是:

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)

当我传递所有空参数值时,出现错误:

  

“无法转换参数值   从String到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;

我该如何解决这个问题?

4 个答案:

答案 0 :(得分:3)

据我所知,你还没有正确构建字符串。如果没有传入@Item_Num,你最终将没有WHERE关键字......你将只有“FROM [tblItem_Log] AND ...”

我会将所有标准追加为“AND ...”,并在初始声明中使用:

FROM [tbl_Item_Log] WHERE (1=1)

由于您有返回生成的字符串的代码,为什么不将它放入SSMS并尝试运行它?

我还注意到,如果你没有传入日期值,那么你最终会执行一个NULL字符串,因为你的最终连接最终会导致NULL。如果您要使用动态SQL构建查询,则需要密切关注这些事情。

一旦我纠正了我能够运行存储过程而没有任何错误(至少生成看起来像一个有效的SQL语句)。这让我相信它可能是基础表中数据类型的问题。你能为它提供定义吗?

最后一点:个人,我会用

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

而不是你所拥有的看似不必要的子查询。

另一个编辑: 您可能希望删除检查LTRIM(@Search_User_ID)&lt;&gt;的代码。 ''。这是一个毫无意义的代码,也许特定于您的服务器/连接的设置导致它因类型不匹配而失败。

答案 1 :(得分:1)

IF (Search_User_ID IS NOT NULL) 

需要变量

前面的@符号

你说你为所有变量传递空字符串但是一个是int,它不能取一个不是int数据的空字符串。不敢相信我没有;第一次注意到。

答案 2 :(得分: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)

答案 3 :(得分:0)

这里确实有几个不同的存储过程。为什么不单独写它们?由switch语句控制的所有内容都可以很容易地在过程代码中。对于LTRIM呼叫也是如此。

您可以使用switch语句从单个SP中调用它们;但我认为首先不合并它们通常会更好。 SP查询将更容易优化,代码将简化。巩固它们并没有太大的收获。

不确定您的业务规则,但您可以使用

简化SQL之外的SQL
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 ...  
}

对于提供或不提供商品编号的情况,您似乎也有单独的逻辑。其他领域也一样。您的每个用例看起来都解析为一个非常简单的查询。