使用用户输入构建SQL查询字符串

时间:2010-04-16 21:54:30

标签: c# asp.net sql sqlcmd

我必须使用用户在网页上选择的值

来创建一个字符串

假设我需要为具有不同搜索条件的多台计算机显示文件...

我目前使用此代码:

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循环来逐个放置多台机器......并根据用户选择的情况进行多次查询...

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

6 个答案:

答案 0 :(得分:2)

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

然后在你的循环中你只需添加1..n机器。 IN子句使用1个元素或n个元素,因此它应该没问题。

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

答案 1 :(得分:2)

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

然后你的SQL将是:

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

完成后,删除userID的所有行:

delete from userMachine where userID = x.

答案 2 :(得分:2)

如果要通过动态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;
}

答案 3 :(得分:1)

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

选项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

答案 4 :(得分:1)

我还建议使用存储过程,否则你会对SQL注入攻击开放 - 尤其是在你根据用户输入构建字符串的情况下。

类似的东西:

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

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

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

答案 5 :(得分:1)

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

示例:

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

如上所述,如果传入NULL或BLANK,则不会将参数添加到查询中。如果您输入了一个值,那么它将用于比较。