根据字符串值搜索int列

问题描述 投票:21回答:9

我在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?

c# sql sql-server tsql sql-server-2014
9个回答
11
投票

Some recommendations

您提供的查询需要优化:

  1. 首先,使用CAST(bkID AS NVARCHAR(MAX))会影响查询的性能,因为它不会使用任何索引,也会对NVARCHAR(MAX)进行强制转换会降低性能。
  2. bkStatus是一个数字列,因此您必须使用=运算符并与数值(0 or 1 or ...)进行比较,所提供的文本值也在asp标记中定义,而不是在数据库中,因此它们用于应用程序级别而不是数据级别。
  3. 如果你使用CAST(bkID AS NVARCHAR(MAX))搜索包含特定数字的bkid列(例如:搜索1 - >结果11011,...),那么尝试转换为特定大小(例如:CAST(bkID as NVARCHAR(10)
  4. 建议使用参数化查询以获得更好的性能并防止Sql注入攻击。看看@ un-lucky答案
  5. 您可以使用字典对象来存储与关键字相关的ID值

Example

注意:使用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 ;

References & Helpful Links


7
投票

所以你需要一个搜索框,用户可以使用bkIDbkSlotbkStatus进行搜索。如果搜索文本是BookedPending,我们必须为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();
     }
}

请注意以下事项:

  • 如果你想包括bookPend等的bkStatus过滤器..那么你必须通过使用.Contains().StartsWith()而不是.ToLower()相应地改变条件
  • 使用-1初始化statusCode,以避免对所有其他值使用基于bkStatus的过滤器

4
投票

您可以使用declare函数创建一个包含bkStatus列表的临时表。

通过使用bkstatus作为外键,您可以更轻松地创建查询。之后,您不必再使用castlike功能了。这将是一个有点低效的。

您可以尝试以下代码:

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  

4
投票

只是另一个选项使用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

3
投票

如果keyword将是状态名称而不是状态ID,我将创建BookingStatus表,在那里有bkStatusbkStatusTitle列并将其加入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执行计划并重用它们,从而提供稍好的性能。


3
投票

你的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 = << ? >>

2
投票

我将只关注你问题的这一部分(这是问题本身吗?):

但是不知道如何搜索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的答案中指出的参数。我们可以在这里讨论最佳实践,我建议您查看以下文章:

  1. Lookup tables:你说bkStatusINT大豆的类型我假设你可以有比预订或待定更多的选项,例如:保留或取消。在这种情况下,您添加的每个选项的实际代码可能会变得越来越不整齐。
  2. Best practices for using ADO.NET:您没有指定如何从前端访问数据库。即使这篇文章已存在多年,但其大部分内容仍然是最新的。我认为这可能会有所帮助。
  3. Building Better Entity Framework:如果您使用Entity Framework访问您的数据库。

希望能帮助到你。


2
投票

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

2
投票

看起来好像你试图在几个列中自由搜索。这是一个非常普遍的问题,真正的解决方案可以在www.Sommarskog.se上找到动态搜索条件。

您的解决方案看起来好像容易受到SQL注入攻击。我可以建议你实现类似于存储过程search_orders_3的东西吗?

© www.soinside.com 2019 - 2024. All rights reserved.