编写可怕的SQL搜索查询(第二阶段)
我正在做一个搜索查询(使用asp.NET3.5前端),它看起来很简单,但很复杂。 完整的查询是:编写可怕的SQL搜索查询(第二阶段),sql,sql-server,sql-server-2005,tsql,dynamic-sql,Sql,Sql Server,Sql Server 2005,Tsql,Dynamic Sql,我正在做一个搜索查询(使用asp.NET3.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_Use
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)
我如何解决这个问题
IF (Search_User_ID IS NOT NULL)
变量前面需要一个@符号
您说您正在为所有变量传递空字符串,但其中一个是int,它不能接受不是int数据的空字符串。不敢相信我没有;我第一次没注意到。为什么不使用如下单参数化查询:
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)
据我所知,你没有完全正确地构建字符串。如果没有传入@Item_Num,您将没有WHERE关键字。。。您只需要“从[tblItem_Log]和…” 我将所有标准的附录都设为“和…”,并且作为您最初的陈述,使用:
FROM [tbl_Item_Log] WHERE (1=1)
既然您有返回生成字符串的代码,为什么不将其放入SSMS并尝试运行它呢
我还注意到,如果不传入日期值,最终将执行一个空字符串,因为最终的连接将导致一个空字符串。如果要使用动态SQL来构建查询,您需要密切关注这些问题
一旦我纠正了这个错误,我就能够在没有任何错误的情况下运行存储过程(至少生成看起来像是有效的SQL语句的内容)。这使我相信底层表中的数据类型可能有问题。你能给出这个定义吗
最后一点:就个人而言,我会使用
CONVERT(VARCHAR(11), RecDate, 106) AS RecDate
而不是您拥有的看似不必要的子查询
又一次编辑:
您可能希望删除检查LTRIM(@Search\u User\u ID)')的代码。这是一段毫无意义的代码,可能是服务器/连接的特定设置导致它失败,因为类型不匹配。这里确实有几个不同的存储过程。为什么不把它们分开写呢?由switch语句控制的所有内容都可以在过程代码中轻松实现。LTRIM调用也是如此 您可以使用switch语句从单个SP调用它们;但我认为一开始最好不要合并它们。SP查询将更容易优化,代码也将简化。通过整合它们,您不会获得多少收益 不确定您的业务规则,但您可以在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 ...
}
另外,对于是否提供项目编号的情况,您似乎有单独的逻辑。其他字段也一样。您的每个用例看起来都可以解析为一个非常简单的查询。请注意,这更简洁,但实际上更慢(性能方面)。在将其插入我的应用程序之前,我需要研究一下您的响应。谢谢你的建议。与动态SQL的性能相比,这是一个倒退。像这样的查询将确保几乎每次都进行硬解析,除非参数完全相同。还有参数嗅探的风险…我需要休假。我没有抓住where条款的问题。就返回的记录数而言,1=1是否会打乱查询?@DotNetRookie:
WHERE 1=1
将得到优化,这是一种很好的方法,可以指定一个WHERE子句,在动态SQL等情况下很容易附加到该子句。这很好。但我仍然得到错误消息。“无法将参数值从字符串转换为Int32。”@DotNetRookie-请发布调用语法。也就是说,当你说你传递所有“空”参数值时,你是什么意思?传递空参数意味着用户没有为Item_Num、Search_Type、Vendor_Num、Search_user_ID etcI输入值。我真的不明白这是如何“几个”不同的存储过程。请你说得具体一点好吗?另外,你正在使用致命的“带前导通配符的字符串”搜索键,这将保证索引扫描或更糟的搜索。当然。每个搜索类型都有一个存储过程。和if(vendornum){}else if(search_user_id){}else{}您在谈论哪个int变量?@search_user_id您需要通过发送NUll非空字符串“”来测试它,不带任何值。查看您的ASP代码。。。您确定下拉列表框的值是整数吗?它是否可能获取显示值而不是索引?在我看来,SQLServer不会在错误消息中给出Int32,这是一个.NET错误。很好。dropdownlist的第一个值是“请选择一个值”,它的索引值不是0。现在我收到另一条错误消息:“将字符串转换为smalldatetime数据类型时,转换失败。”
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 ...
}