【问题标题】:Why does my SQL query not work as I want to?为什么我的 SQL 查询不能按我的意愿工作?
【发布时间】:2021-11-28 06:26:13
【问题描述】:

我正在开发一个在 ASP.NET Web 应用程序(框架 4.8)中进行用户管理的项目。在代码中创建了搜索框,我希望能够在其中搜索名称、员工编号以及它们是否被排除。我将它用作过滤器,因此我不需要滚动浏览 gridview 中的所有页面。我在查询中尝试了一些更改,但似乎没有什么不同。我对这类 SQL 查询不是很熟悉。

所以我想知道是否有人可以简单地向我解释我的查询出了什么问题以及为什么?

PopulateGridView:

 void PopulateGridView()
        {
            string find = "select * from TBL_USERS where (Name like '%' + @Name + '%') or (employee like '%' + @Employee + '%') and (case when excluded =1 then 'True' else 'False' end like '%false%')";
            SqlCommand comm = new SqlCommand(find, con);
            comm.Parameters.Add("@Name", SqlDbType.VarChar, 255).Value = TextBox1.Text;
            comm.Parameters.Add("@Employee", SqlDbType.VarChar, 32).Value = TextBox1.Text;
            comm.Parameters.Add("@Excluded", SqlDbType.Bit, 1).Value = 1;
            con.Open();
            comm.ExecuteNonQuery();
            SqlDataAdapter da = new SqlDataAdapter();
            da.SelectCommand = comm;
            DataSet ds = new DataSet();
            da.Fill(ds, "Name");
            da.Fill(ds, "Employee");
            da.Fill(ds, "Excluded");
            gvTestUsers.DataSource = ds;
            gvTestUsers.DataBind();
            con.Close();
            
        }

【问题讨论】:

  • “不工作”是什么意思?另外,为什么WHERE 中的CASE 表达式而不仅仅是AND excluded = 0
  • 您实际看到的是什么,这与您的预期有何不同?注意:最后一个子句(带有case when excluded)看起来非常可疑——尤其是如果数据库区分大小写——为什么不只是对excluded=@Excluded 进行测试呢?此外,如果 @Employee 是员工编号 - 您可能想要使用 =,而不是 like
  • 不要在不使用 ( ) 的情况下混合 AND 和 OR 来准确指定您想要的与和/或 orred。小心将% 烘焙到查询中;最好将它放在 parameter 中,这样您就可以选择如何匹配这些值。你如何拥有它总是会做一个“包含”,但如果你把它放在参数中,你可以通过在文本框中发送 "John%" 的值来做一个“开始”。现在,如果他们将姓名框留空并在员工框中写入,他们无论如何都会得到所有记录,因为name like '%%' 为真(姓名为空除外)
  • @CaiusJard re "整个代码应该有大约 6 行" - gvTestUsers.DataSource = con.Query<User>(@"select * from TBL_USERS where ...", new { Name = TextBox1.Text, Employee = TextBox1.Text, Excluded = true }).AsList(); - 一行 :)
  • 最后的那个东西'(排除的情况=1然后'真'否则'假'结束像'%false%')'你可以只使用排除 1吗?此外,您的排除仅适用于员工搜索,而不适用于姓名搜索。这是因为 AND 在其评估顺序中优先于 OR。

标签: c# asp.net sql-server


【解决方案1】:

当我在框中输入名称时,gridview 将显示所有具有该名称的用户。当我在框中输入一个 Employeenumber 时,它只会显示一个具有该 Employeenumber 的用户

我个人会使用不同的查询,例如EmployeeNumber 是一个整数/您可以从他们输入的内容中看出用户想要什么。尝试在有一堆参数的情况下进行动态查询,这些参数都是 ORred 计划通常非常糟糕并且性能很差。 Building an SQL with a targeted where clause 比用 N 个不同的 ORred 参数捏造一些东西更受欢迎。

我会使用 Dapper:

if(!int.TryParse(TextBox.Text)) //by name
  gvTestUsers.DataSource = con.Query<User>(
    @"select * from TBL_USERS where Name LIKE @Name AND excluded=0", 
    new { Name = "%" + TextBox.Text + "%" }
  ).AsList();

else //employee 
  gvTestUsers.DataSource = con.Query<User>(
    @"select * from TBL_USERS where EmployeeNumber = @Emp AND excluded=0", 
    new { Emp = TextBox.Text }
  ).AsList();

如果 EmployeeNumber 是字母数字(并且您确实在“包含”之后)并且您无法判断他们是否输入了姓名或号码,那么 OR 可能是您必须采用的方式:

gvTestUsers.DataSource = con.Query<User>(
  @"select * from TBL_USERS where (Name LIKE @X OR EmployeeNumber LIKE @X) AND excluded=0", 
  new { X= "%" + TextBox.Text + "%" }
).AsList();

是的.. 这就是你对 Dapper 所做的一切(在创建 SqlConnection con 之后) - 它处理所有参数,运行查询,检索结果,将它们变成你的 User 类的实例。 .(我相信你有,对吧?如果你使用的是支持记录的 C# 版本,就像record User(string Name, int EmployeeNumber, ...) 一样简单)

相比之下,使用数据表要痛苦得多;一切都是 stringly 类型的,需要一直从对象转换.. 糟糕

--

但是,如果您确实想继续使用 SqlDataAdapter,它看起来像:

var dt = new DataTable;
var da = new SqlDataAdapter("select * from TBL_USERS where (Name LIKE @X OR EmployeeNumber LIKE @X) AND excluded=0", connstringhere);
da.SelectCommand.Parameters.Add("@X", SqlDbType.VarChar, 255).Value = "%"+ TextBox.Text + "%";
da.Fill(dt);
gvTestUsers.DataSource = dt;
gvTestUsers.DataBind();

如果您希望名称匹配模糊,但没有。准确的匹配,将查询翻转到Name LIKE '%' + @X + '%' OR EmployeeNumber = @X

关于数据适配器是否需要处理存在一些争论;微软在他们的例子中没有,但有些人觉得“它是 IDisposable,应该被丢弃”——如果你属于那个阵营,在 var 前面添加 using 会这样做

【讨论】:

    【解决方案2】:

    您的主要问题似乎是OR 周围缺少括号。尚不完全清楚您想要什么样的过滤:是要搜索任一值,还是只想搜索非空值。您可能需要调整条件

    您的 C# 代码应如下所示

    void PopulateGridView()
    {
        const string find = @"
    select *
    from TBL_USERS
    where (Name like '%' + @Name + '%' or employee like '%' + @Employee + '%')
    and excluded = @Excluded
    ";
        using(var con = new SqlConnection(YourConnString))
        using(var comm = new SqlCommand(find, con))
        {
            comm.Parameters.Add("@Name", SqlDbType.VarChar, 255).Value = TextBox1.Text;
            comm.Parameters.Add("@Employee", SqlDbType.VarChar, 32).Value = TextBox1.Text;
            comm.Parameters.Add("@Excluded", SqlDbType.Bit).Value = 1;
            con.Open();
            var dt = new DataTable();
            using(var reader = comm.ExecuteReader())
            {
                dt.Load(reader);
            }
            gvTestUsers.DataSource = dt;
            gvTestUsers.DataBind();
        }            
    }
    
    • 注意连接对象的使用,而不是缓存对象
    • 注意using
    • 请注意,查询字符串是 const,这使您不太可能渴望将数据注入其中
    • 由于你只有一张表,你可以把它放到DataTable
    • 适配器仅在您有要合并的现有数据时才有用

    【讨论】:

    • 只有当你有现有数据要合并时,适配器才有用——绝对不是这样。你的dt.Load 在内部使用了一个适配器。使用适配器,这段代码会简单得多
    • @CaiusJard 并没有那么简单,因为您仍然需要创建一个适配器并加载到DataTableDataSet,因此您不会保存任何代码行
    • 但是数据表有一个加载方法——你可以去 dt.Load(comm.ExecuteReader()) ,因此不需要阅读器或适配器——它内置在命令对象中。所以没有必要创建一个名为 reader 的 var。
    • @AlbertD.Kallal 是的,如果您使用适配器,则不需要任何它。但是 Charlieface 想要处置阅读器,因此 using - 和 这些十年 有比所有这些冗长的字符串类型的废话更好的方法
    • EF 的额外负载通常会耗费精力和时间。通常在你和数据之间进行额外的工作并在你和数据之间放置一个类有一些不容忽视的缺点。问题是必须在代码中创建一个适配器 - 并不是说​​ ExecuteReader 使用一个,这在这里有很大的不同。它必须编码。通常,仅当您“调整”或修改数据表中的数据时才需要在代码中定义的适配器。如果不修改数据表,那么很少需要在代码中定义适配器。然而,哦,这么多代码示例没有充分的理由。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 2022-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多