【问题标题】:Build SQL query string using user input使用用户输入构建 SQL 查询字符串
【发布时间】:2010-04-16 21:54:29
【问题描述】:

我必须使用用户在网页上选择的值来创建一个字符串,

假设我需要用不同的搜索条件显示多台机器的文件...

我目前使用这个代码:

DataTable dt = new DataTable();
SqlConnection connection = new SqlConnection();
connection.ConnectionString = ConfigurationManager
               .ConnectionStrings["DBConnectionString"].ConnectionString;
connection.Open();
SqlCommand sqlCmd = new SqlCommand
  ("SELECT FileID FROM Files
    WHERE MachineID=@machineID and date= @date", connection);
SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCmd);

sqlCmd.Parameters.AddWithValue("@machineID", machineID);
sqlCmd.Parameters.AddWithValue("@date", date);

sqlDa.Fill(dt);

现在这是一个固定查询,用户只有一台机器,只选择一个日期...

我想做一个查询,其中用户有多个搜索选项,如类型或大小,如果他/她想要,这取决于他/她选择的内容。

如果他/她可以选择多台机器...

SELECT FileID FROM Files
WHERE (MachineID=@machineID1 or MachineID = @machineID2...)
AND (date= @date and size=@size and type=@type... )

所有这些都发生在运行时...否则我必须创建一个for 循环来将多台机器一一放置...并根据用户选择的情况进行多个查询...

这很有趣,我可以使用一些帮助...

【问题讨论】:

  • 您的输入很棒,但是根据用户选择添加日期、类型等其他搜索条件呢……好吧,我不擅长存储过程,所以正在寻找一个简单的出路...但是正如你们所有人建议我走的那样,我需要知道如何实现它..有人可以告诉我在哪里可以找到有关存储过程的好信息,以便我可以理解和使用它...
  • 嗨,我已经扩展了我的答案,包括一个可以解决您的问题的存储过程。

标签: c# asp.net sql sqlcmd


【解决方案1】:

如果您打算通过动态 SQL 执行此操作,则需要构建对 IN 函数的调用。 (例如 In(id1, id2, id3...)

private string GetSql( IList<int> machineIds )
{
    var sql = new StringBuilder( "SELECT FileID FROM Files Where MachineID In(" );
    for( var i = 0; i < machineIds.Count; i++ )
    {
        if ( i > 0 )
            sql.Append(", ")
        sql.Append("@MachineId{0}", i);
    }

    sql.Append(" ) ");

    //additional parameters to query
    sql.AppendLine(" And Col1 = @Col1" );
    sql.AppendLine(" And Col2 = @Col2 ");
    ...

    return sql.ToString();
}

private DataTable GetData( IList<int> machineIds, string col1, int col2... )
{
    var dt = new DataTable();
    var sql = GetSql( machineIds );
    using ( var conn = new SqlConnection() )
    {
        conn.ConnectionString = ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
        using ( var cmd = new SqlCommand( sql, conn ) )
        {
            conn.Open();

            for( var i = 0; i < machineIds.Count; i++ )
            {
                var parameterName = string.Format("@MachineId{0}", i );
                cmd.Parameters.AddWithValue( parameterName, machineIds[i] );
            }

            cmd.Parameters.AddWithValue( "@Col1", col1 ); 
            cmd.Parameters.AddWithValue( "@Col2", col2 ); 
            ...

            using ( var da = new SqlDataAdapter( cmd ) )
            {
                da.Fill( dt );
            }
        }
    }

    return dt;
}

【讨论】:

  • 好的,我想我理解了你的代码,但还没有实现它,但是对其他搜索项有什么作用..只需像你对“,”和“machineID”所做的那样附加它们???
  • @user175084 - 如果“其他搜索项”是指查询中的其他列,您可以像使用 MachineId = @MachineId 时那样将它们构建到 Select 语句中。所以,“...和 ​​Col1 = @Col1 和 Col2 = @Col2 ...”。如果“其他”是指其他机器 ID,则将列表传递给要返回的 Id 的函数。
  • @user175084 - 我已经编辑了我的回复,以展示您如何将其他列传递到查询中。您需要将参数附加到您希望放入查询中的每个附加值的 SQL 语句,然后为每个附加值添加 SqlParameters。
  • 好吧,看起来不错,让我试试这个,希望它有效......谢谢男人
  • 我尝试了很多东西,但我得到了这个错误必须声明标量变量“@MachineId”
【解决方案2】:

您可以使用WHERE MachineID IN ('Machine1', 'Machine2', 'Machine3', ... 'MachineN')

然后在您的循环中,您只需添加 1..n 台机器。 IN 子句适用于 1 个元素或 n 个元素,所以应该没问题。

但是,我会考虑使用存储过程来执行此操作,而不是将 SQL 硬编码到您的应用程序中。

【讨论】:

  • 这可能容易受到 SQL 注入的攻击,因为您不再使用参数化查询(至少,我从未见过允许参数化“in”子句的参数化查询接口)
  • @meador - 我同意 100%,这就是我建议使用存储过程的原因。
【解决方案3】:

构建一个真实的表并将机器ID加载到其中。

那么您的 SQL 将是:

where MachineID in ( select MachineID from userMachine where userID = x)

完成后,删除用户 ID 的所有行:

delete from userMachine where userID = x.

【讨论】:

    【解决方案4】:

    通常,当我想创建“搜索”类型的查询时,我使用可选参数。这使我可以向参数发送任何内容或不发送任何内容,从而使查询从模糊变为非常具体。

    例子:

    SELECT
      COL1,
      COL2,
      COL3
    FROM TABLE
    WHERE (@COL1 IS NULL OR @COL1 = '' OR @COL1 = COL1)
    

    正如您在上面所注意到的,如果您传入 NULL 或 BLANK,它不会将参数添加到查询中。如果您确实输入了一个值,那么它将用于比较。

    【讨论】:

      【解决方案5】:

      理想情况下,您试图找到类似于动态创建“MachineID in (1, 2, 3, 4)”的解决方案。

      选项 1

      有很多方法可以完成这个任务,将逗号分隔的字符串传入存储过程并动态构建sql字符串,然后调用“EXEC sp_executesql @sql” WHERE IN (array of IDs)

      选项 2

      你可以传入一个逗号分隔值的字符串,然后将这些值解析到它们自己的临时表中,然后加入它 http://vyaskn.tripod.com/passing_arrays_to_stored_procedures.htm

      选项 3 - 我的选择

      您现在可以使用 XML 传入值数组,然后轻松选择数组项。 http://support.microsoft.com/kb/555266

      .

      【讨论】:

        【解决方案6】:

        我还建议使用存储过程,否则您将面临 SQL 注入攻击 - 尤其是在基于用户输入构建字符串的情况下。

        类似:

        a' or 1=1; -- Do bad things
        

        您可以在 SQL 中使用 sp_executesql 来运行由 @dcp 建议的 where 子句构建的 SQL 语句,虽然它不会很好地优化,但它可能是一个快速运行的命令。

        SQL Injection attacks by example

        实现此目的的一种方法是使用 charindex。这个例子演示了当传递一个以空格分隔的 id 列表时如何运行存储过程:

        declare @machine table (machineId int, machineName varchar(20))
        declare @files table (fileId int, machineId int)
        
        insert into @machine (machineId, machineName) values (1, 'machine')
        insert into @machine (machineId, machineName) values (2, 'machine 2.0')
        insert into @machine (machineId, machineName) values (3, 'third machine')
        insert into @machine (machineId, machineName) values (4, 'machine goes forth')
        insert into @machine (machineId, machineName) values (5, 'machine V')
        
        insert into @files (fileId, machineId) values (1, 3)
        insert into @files (fileId, machineId) values (2, 3)
        insert into @files (fileId, machineId) values (3, 2)
        insert into @files (fileId, machineId) values (4, 1)
        insert into @files (fileId, machineId) values (5, 3)
        insert into @files (fileId, machineId) values (6, 5)
        
        declare @machineText1 varchar(100)
        declare @machineText2 varchar(100)
        declare @machineText3 varchar(100)
        
        set @machineText1 = '1 3 4'
        set @machineText2 = '1'
        set @machineText3 = '5 6'
        
        select * from @files where charindex(rtrim(machineId), @machineText1, 1) > 0
        -- returns files 1, 2, 4 and 5
        
        select * from @files where charindex(rtrim(machineId), @machineText2, 1) > 0
        -- returns file 4
        
        select * from @files where charindex(rtrim(machineId), @machineText3, 1) > 0
        --returns file 6
        

        所以你可以创建这个存储过程来实现你的目标:

        create procedure FilesForMachines (@machineIds varchar(1000))
        as
        select * from [Files] where charindex(rtrim(machineId), @machineIds, 1) > 0
        

        charindex 提示来自BugSplat

        【讨论】:

          猜你喜欢
          • 2021-07-15
          • 1970-01-01
          • 1970-01-01
          • 2015-12-09
          • 1970-01-01
          • 2013-03-21
          • 1970-01-01
          • 1970-01-01
          • 2016-05-10
          相关资源
          最近更新 更多