编写一个可怕的SQL搜索查询(第二阶段)
我正在研究一个搜索查询(带有一个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;
我该如何解决这个问题?
你不太建设字符串正确,据我可以告诉。如果没有传入@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)代码<>'。这是一个毫无意义的代码,可能是一个特定于您的服务器/连接的设置导致它由于类型不匹配而失败。
我需要一个假期。我没有看到where子句问题。就返回的记录数而言,1 = 1是否会混淆查询? – DotNetRookie 2010-06-29 18:14:45
@DotNetRookie:'WHERE 1 = 1'将被优化,并且是一个很好的方法来指定一个WHERE子句,这个子句很容易附加到动态SQL等情况。 – 2010-06-29 18:18:13
这很好。但我仍然得到那个错误消息。 “无法将参数值从字符串转换为Int32。” – DotNetRookie 2010-06-29 18:22:15
IF (Search_User_ID IS NOT NULL)
需要一个@符号盈变量
你说你正在传递空字符串的所有变量,但一个是int型的,它不能把未int类型数据的空字符串。不敢相信我没有注意到,那是第一次。
好的。完成:-) – DotNetRookie 2010-06-29 18:06:52
你在谈论哪个int变量? – DotNetRookie 2010-06-29 19:04:00
@Search_User_ID 您需要通过在NUll中发送非空字符串''来测试它没有值。 – HLGEM 2010-06-29 19:16:41
你为什么不使用单一的参数化查询是这样的:
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)
只是说明这是更干净,但实际上更慢(性能明智)。 – JonH 2010-06-29 18:12:15
在我将其插入我的应用程序之前,我需要研究一下您的回复。感谢您的建议。 – DotNetRookie 2010-06-29 18:15:57
与动态SQL的性能相比,这是一个向后*步*。像这样的查询将确保几乎每次都难以解析,除非参数完全相同。还有参数嗅探的风险... – 2010-06-29 18:28:53
这里确实有几个不同的存储过程。为什么不把它们分开写呢?所有由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看起来你对其中项目数量提供或不提供单独的情况下的逻辑。其他领域也一样。你的每个用例看起来像解析为一个非常简单的查询。
我真的不知道“三种不同的存储过程”。 – DotNetRookie 2010-06-29 18:35:27
重写为“几个”。 – dkretz 2010-06-29 18:37:03
我不明白这是“几个”不同的存储过程。你能有点特别吗? – DotNetRookie 2010-06-29 18:39:26
看着你的ASP代码...你确定下拉列表框的值是整数吗?它可能是抓取显示值而不是索引?正如我想的那样,SQL Server不会在错误消息中给出Int32 - 这是一个.NET错误。 – 2010-06-29 19:28:58
好的。下拉列表的第一个值是“请选择一个没有索引值为0的值”。现在我收到另一条错误消息:“将字符串转换为smalldatetime数据类型时转换失败。” – DotNetRookie 2010-06-29 19:34:30