【问题标题】:Sql filter and or statement within C# applicationC# 应用程序中的 Sql 过滤器和或语句
【发布时间】:2014-06-26 14:32:43
【问题描述】:

我有一个 C# 应用程序,其中我遇到了这个问题:当我运行这个 sn-p 时:

第一道

public void GetList(List<string> liste, List<int> outliste)
{
    foreach( string s in liste){
        outliste.Add(SqlFunction(s));
    }
}
public int SqlFunction(string str)
{
    string query = "select id from user where name="+str;
    ...................
    // return the id
}

执行时间为 51 秒

第二种方式

public void SqlSecondWayFunction(List<string> liste, List<int> outliste)
{

   string query ="select id from user where (";
   foreach(string str in liste){
       query += "name=" str + "or  ";
   }
   query += " 1=0 )";

   ...................
   // fill outliste by the result of the query
}

执行时间是1m:19sec!!!!!!!!! (liste 的计数约为 11000)。

所以我需要知道为什么第一种方法更快?

【问题讨论】:

  • 你知道他们都对 SQL 注入开放?
  • @user2711965 谢谢我知道,这只是一个简单的示例
  • 第二个示例连接所有 where 块并将它们与OR 链接。然后在最后,将1=1 附加到同样与OR 链接的where 语句。这意味着它返回所有行,因为 1=1 始终为真
  • 我更想知道为什么差异如此之小。具有包含数千个运算符的条件的查询应该需要更长的时间才能运行......
  • 我怀疑第二种方法的主要问题是使用query += 构建您的查询,在每个循环中,这会在堆上创建一个新字符串,而不仅仅是添加到现有字符串。请改用 StringBuilder。话虽如此,将列表作为参数传递有更好、更安全的方法。您使用的是什么 DBMS?

标签: c# sql .net collections ado.net


【解决方案1】:

第一个更快,因为它做的事情更少。而且它只是选择记录的一个子集。

第二个比较慢,因为您要连接大量字符串(这样做相当慢)以及对数据库执行数以千计的不必要比较 every 记录,只是为了返回 每条记录。

基本上,您要求数据库将 name 列与表中每条记录的 11,000 个字符串进行比较。例如,如果表包含 100,000 条记录,那么您将进行 1,100,000,000 字符串比较。然后你无论如何都会返回所有记录,因为你的条件之一是“1 = 1”,它总是为真。

【讨论】:

  • 谢谢,我更改了1=1(见我的编辑),我使用字符串生成器而不是字符串,这次我得到了 54 秒
  • @Lamloumi:现在它返回的记录更少了。但它仍在执行大量字符串连接和字符串比较。你到底在问什么?
  • 我在问为什么第一种方式仍然是最快的,即使我在每次调用该方法时都连接/关闭连接,而第二种方式只有一次我过滤列表以获得结果,它是在 DBMS 方面
  • ADO.NET 使用connection pooling,因此即使您在代码中打开/关闭连接,ADO.NET 也有可能一遍又一遍地重用相同的连接,所以您实际上并没有获得打开和关闭连接 11,000 次的成本。
【解决方案2】:

一些事情:

  • 在 WHERE 子句中添加一长串 OR 通常效率不高,因为首先解析一个非常长的查询字符串需要时间,而且数据库引擎为一长串 OR 完成的内部工作
  • 您添加的最后一部分(“OR 1=1”)使 WHERE 子句始终为真!这和根本没有 WHERE 子句是一样的
  • 以上任何一种情况都可能导致您的第一个示例中的许多简单查询比一个低效的大型查询花费的时间更少
  • 您应该为您的用例考虑以下其中一项:1) 通过使用“从用户选择 id”加载 DataTable、调用 DataTable.AsEnumerable() 方法,然后加入您的其他列表来获取 id 列表使用 LINQ 查询,2) 首先将列表加载到数据库表中,然后使用 SQL 将用户表连接到列表中,或者 3) 将列表加载到以逗号分隔的字符串列表中,并使用 SQL 的 IN 运算符代替你的 name=x OR name=y OR name=z 构造。

【讨论】:

    【解决方案3】:

    如果您想将列表传递给 SQL Server 2008,请使用 table-valued parameters。首先需要在您的数据库上创建一个新的 TYPE:

    CREATE TYPE dbo.ListOfInt AS TABLE(Value INT);
    

    然后您可以将其用作参数,例如:

    var table = new DataTable();
    table.Columns.Add("Value", typeof(int));
    
    for (int i = 0; i < liste.Count; i++)
    {
        var row = table.NewRow();
        row[0] = liste[i];
        table.Rows.Add(row);
    }
    
    string sql = "SELECT ID FROM [User] WHERE ID IN (SELECT Value FROM @Liste)";
    
    using (var connection = new SqlConnection("Your connection String"))
    using (var command = new SqlCommand(sql, connection))
    {
        connection.Open();
        var tvp = new SqlParameter("@Liste", SqlDbType.Structured).TypeName = "ListOfInt";
        tvp.Value = table;
        command.Parameters.Add(tvp);
        using (var reader = command.ExecuteReader())
        while (reader.Read())
        {
            outliste.Add(reader.GetInt("ID"));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-01
      • 2016-05-06
      • 1970-01-01
      相关资源
      最近更新 更多