2010-04-16 80 views
4

我必须使用用户在网页上选择的值来创建一个字符串,使用用户输入构建SQL查询字符串

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

我目前使用此代码:

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 [email protected] 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 ([email protected] or MachineID = @machineID2...) 
AND (date= @date and [email protected] and [email protected]) 

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

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

+0

您的输入是伟大的,但其他搜索条件添加像日期,类型等取决于用户的选择....确定我不擅长存储过程,所以正在寻找一个简单的出路...但作为allu你建议我去那种方式,我需要知道如何实现它..可以some1告诉我在哪里可以找到关于存储过程的好信息,以便我可以理解和使用它... – user175084 2010-04-16 22:23:00

+1

嗨,我'我已经扩展了我的答案,包括一个可以解决您的问题的存储过程。 – amelvin 2010-04-16 23:39:29

回答

2

如果你想通过动态SQL要做到这一点,你需要建立一个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; 
} 
+0

好吧,我想我了解你的代码,但没有实现它,但是对于其他搜索项目呢。只需将它们追加为“,”和“machineID”? – user175084 2010-04-16 22:34:46

+0

@ user175084 - 如果通过“其他搜索项目”指查询中的其他列,则可以将它们构建到Select语句中,就像您使用'MachineId = @ MachineId'时一样。所以,“...和Col1 = @ Col1和Col2 = @ Col2 ...”。如果“other”表示其他机器标识,则将一个列表传递给您要返回的标识的功能。 – Thomas 2010-04-16 22:41:06

+1

@ user175084 - 我编辑了我的回复,以显示如何将其他列传递到查询中。您需要将参数添加到您的SQL语句中,以查找每个希望放入查询的附加值,然后为每个附加值添加SqlParameters。 – Thomas 2010-04-16 22:46:09

2

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

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

但是,我会看看使用存储过程来做到这一点,而不是将SQL硬编码到您的应用程序中。

+1

由于您不再使用参数化查询(至少,我从来没有见过允许参数化“in”子句的参数化查询接口),因此这可能容易受到SQL注入的影响 – rmeador 2010-04-16 22:03:57

+0

@meador - 我同意100%这就是为什么我建议使用存储过程。 – dcp 2010-04-16 22:14:43

+2

什么是SQL注入...? – user175084 2010-04-16 22:24:11

1

理想情况下,您尝试获得类似于动态创建“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

+1

我不知道为什么我不能给你一个+投票...它说投票太老.. – user175084 2010-04-19 16:56:10

+1

@ user175084现在试一下,我编辑它。 – Glennular 2010-04-19 18:41:54

2

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

那么你的SQL是:

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

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

delete from userMachine where userID = x. 
1

我也建议使用存储过程,因为否则你将会把自己暴露给SQL注入攻击 - 尤其是你正在建立一个基于字符串的用户输入

喜欢的东西:

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

您可以使用sp_executesql的SQL中运行的基石,也是与像@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

+0

这是有帮助的....谢谢 – user175084 2010-04-16 22:31:16

+1

不用担心,祝你好运与你的任务。 – amelvin 2010-04-16 23:24:11

1

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

例子:

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

正如你会发现上面的,如果你在空或没有通过它不会将参数添加到查询。如果你输入一个值,那么它将用于比较。