我在sql server 2014中有一个View View_Booking
:
bkID bkSlot bkStatus
---- ------ --------
2 Lunch 1
4 Lunch 1
6 Dinner 0
7 Lunch 1
在c#中我使用了gridview并将bkStatus
转换为字符串,如:
<asp:Label ID="lblStatus" Text='<%# (Eval("bkStatus")+"" == "1") ? "Booked" : "Pending" %>'
... ></asp:Label>
bkID bkSlot bkStatus
---- ------ --------
2 Lunch Booked
4 Lunch Booked
6 Dinner Pending
7 Lunch Booked
现在我正在使用此查询搜索View:
SELECT * FROM View_Booking
WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE '%" + keyword + "%'
OR bkSlot LIKE '%"+keyword+"%'
OR bkStatus LIKE << ? >>
但是不知道如何搜索从c#作为字符串传递的bkStatus
,而它是sql中的int?
您提供的查询需要优化:
CAST(bkID AS NVARCHAR(MAX))
会影响查询的性能,因为它不会使用任何索引,也会对NVARCHAR(MAX)
进行强制转换会降低性能。bkStatus
是一个数字列,因此您必须使用=
运算符并与数值(0 or 1 or ...)
进行比较,所提供的文本值也在asp
标记中定义,而不是在数据库中,因此它们用于应用程序级别而不是数据级别。CAST(bkID AS NVARCHAR(MAX))
搜索包含特定数字的bkid
列(例如:搜索1
- >结果1
,10
,11
,...),那么尝试转换为特定大小(例如:CAST(bkID as NVARCHAR(10)
)注意:使用CAST和Like将不使用任何索引,此示例基于您的要求(我尝试将我提供的建议与其他建议结合起来)
var dicStatus = new Dictionary<int, string> {
{ 0, "Pending" },
{ 1, "Booked" },
{ 2, "Cancelled" }
// ...
};
string querySql = " SELECT * FROM View_Booking" +
" WHERE CAST(bkID AS NVARCHAR(10)) LIKE @bkID" +
" OR bkSlot LIKE @bkSlot" +
" OR bkStatus = @status";
using (SqlConnection dbConn = new SqlConnection(connectionString))
{
dbConn.Open();
using (SqlCommand sqlCommand = new SqlCommand(querySql, dbConn))
{
sqlCommand.Parameters.Add("@bkID", SqlDbType.VarChar).value ="%" + keyword + "%";
sqlCommand.Parameters.Add("@bkSlot", SqlDbType.VarChar).value ="%" + keyword + "%";
sqlCommand.Parameters.Add("@status", SqlDbType.Int).value = dicStatus.FirstOrDefault(x => x.Value == keyword).Key;
sqlCommand.ExecuteNonQuery();
}
}
此外,如果BkID是整数列,则最好使用
sqlCommand.Parameters.Add("@bkID", SqlDbType.Int).value = (Int)keyword ;
所以你需要一个搜索框,用户可以使用bkID
,bkSlot
或bkStatus
进行搜索。如果搜索文本是Booked
或Pending
,我们必须为bkStatus
添加过滤器,这将是数据库中的整数字段。对?我在这里要提到的更多事情是using
的使用以及查询的参数化,以实现更智能和更安全的执行方式。所以我想建议如下构建和执行查询:
int statusCode = -1;
if(keyword.ToLower() == "booked")
statusCode = 1;
else if(keyword.ToLower() == "pending")
statusCode = 0;
string querySql = " SELECT * FROM View_Booking" +
" WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE @bkID" +
" OR bkSlot LIKE @bkSlot" +
" OR bkStatus = @status";
using (SqlConnection dbConn = new SqlConnection("connectionString here"))
{
dbConn.Open();
using (SqlCommand sqlCommand = new SqlCommand(querySql, dbConn))
{
sqlCommand.Parameters.Add("@bkID", SqlDbType.VarChar).value ="%" + keyword + "%";
sqlCommand.Parameters.Add("@bkSlot", SqlDbType.VarChar).value ="%" + keyword + "%";
sqlCommand.Parameters.Add("@status", SqlDbType.int).value = statusCode;
sqlCommand.ExecuteNonQuery();
}
}
请注意以下事项:
book
,Pend
等的bkStatus过滤器..那么你必须通过使用.Contains()
或.StartsWith()
而不是.ToLower()
相应地改变条件-1
初始化statusCode,以避免对所有其他值使用基于bkStatus
的过滤器您可以使用declare
函数创建一个包含bkStatus
列表的临时表。
通过使用bkstatus
作为外键,您可以更轻松地创建查询。之后,您不必再使用cast
或like
功能了。这将是一个有点低效的。
您可以尝试以下代码:
declare @bkstatus table (number int primary key , bkstatus varchar(10) )
insert into @bkstatus (number , bkstatus)
values ( 0 , 'Pending'), (1 , 'Booked')
然后使用此查询:
SELECT * FROM View_Booking v
INNER JOIN @bkstatus b on v.bkstatus = b.number
WHERE b.bkstatus = @keyword
只是另一个选项使用CHOOSE()解码bkStatus和TRY_CONVERT()来测试bkID。
例
Declare @KeyWord varchar(50) = 'Pending';
Select *
From View_Booking
Where bkID = try_convert(int,@KeyWord)
or bkSlot like '%'+@KeyWord+'%'
or choose(bkStatus+1,'Pending','Booked')=@KeyWord
返回
bkID bkSlot bkStatus
6 Dinner 0
如果keyword
将是状态名称而不是状态ID,我将创建BookingStatus
表,在那里有bkStatus
和bkStatusTitle
列并将其加入View_Booking
。你可以轻松地对bkStatusTitle
做点赞。
SELECT * FROM View_Booking
WHERE CAST(bkID AS NVARCHAR(16)) LIKE '%' + @keyword + '%'
OR bkSlot LIKE '%' + @keyword + '%'
OR bkStatusTitle LIKE '%' + @keyword + '%'
如果keyword
将是bkStatus
的字符串表示,我只会看看值是否相同。
作为旁注,构建SQL查询将用户输入连接到'%' + keyword + '%'
是一个坏主意。这对SQL注入攻击是开放的。最好使用SQL参数将用户输入传递给SQL查询。在SQL位和C#中使用'%' + @keyword + '%'
就像下面的示例一样,会更加安全。
sqlCommand.Parameters.Add("@keyword", SqlDbType.VarChar, 1000);
sqlCommand.Parameters["@keyword"].Value = searchText;
参数化查询还为多个请求提供相同查询文本的好处,这反过来允许SQL Server缓存SQL执行计划并重用它们,从而提供稍好的性能。
你的bkStatus是整数。在视图中,您将整数值转换为用户有意义的字符串。到目前为止一切都好。现在,对于用户搜索所有你需要做的是将你的翻译从字符串转换为整数并搜索整数。
保持简单
searchStatusKey = yourVariableHodingTheString == "Booked" ? 1 : 0;
但是,为了避免在多个地方出现繁琐的错误和无痛的代码升级(比如因为他们决定在那里添加另一个状态),我建议在这里使用转换表。像HashMap(关联数组)之类的东西。
var statusMap = new Dictionary<int, string> {
{ 0, "Pending" },
{ 1, "Booked" },
/* can always add more pairs in the future as needed */
};
现在,假设您的参数位于名为searchStatus
的变量中
searchStatusKey = statusMap.FirstOrDefault(x => x.Value == searchStatus).Key;
现在在searchStatusKey
值的where部分提供bkStatus参数并完成。
select * from View_Booking where bkStatus = << ? >>
我将只关注你问题的这一部分(这是问题本身吗?):
但是不知道如何搜索bkStatus,它是从c#传递的字符串,而它是sql中的int?
在SQL中处理它的一种方法是在CASE子句的帮助下。在您的具体情况下,您可以(并不意味着应该)执行以下操作:
SELECT * FROM View_Booking
WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE '%" + keyword + "%'
OR bkSlot LIKE '%"+keyword+"%'
OR bkStatus = CASE '%"+keyword+"%' WHEN 'Booked' THEN CAST(1 AS INT) WHEN 'Pending' THEN CAST(0 AS INT) WHEN ... THEN ... ELSE ... END'
但我建议使用@ un-lucky的答案中指出的参数。我们可以在这里讨论最佳实践,我建议您查看以下文章:
bkStatus
是INT
大豆的类型我假设你可以有比预订或待定更多的选项,例如:保留或取消。在这种情况下,您添加的每个选项的实际代码可能会变得越来越不整齐。希望能帮助到你。
为enum
创建一个BookingStatus
并创建一个接受字符串并返回枚举值的函数。请参阅以下代码。
public enum BookingStatus {
[Description("Pending")]
Pending = 0,
[Description("Booked")]
Booked = 1
}
现在功能如下,
public static T GetValueFromDescription<T>(string p_description)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute != null)
{
if (attribute.Description == p_description)
return (T)field.GetValue(null);
}
else
{
if (field.Name == p_description)
return (T)field.GetValue(null);
}
}
throw new ArgumentException("Not found.", "description");
// or return default(T);
}
现在在sql查询中的参数中,使用"Booked"
或"Pending"
参数调用此函数,它将返回枚举BookingStatus.Booked
。您可以轻松地从中提取int
值。
(int)BookingStatus.Booked // will give 1
看起来好像你试图在几个列中自由搜索。这是一个非常普遍的问题,真正的解决方案可以在www.Sommarskog.se上找到动态搜索条件。
您的解决方案看起来好像容易受到SQL注入攻击。我可以建议你实现类似于存储过程search_orders_3的东西吗?