2010-04-16 79 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) 

正如你會發現上面的,如果你在空或沒有通過它不會將參數添加到查詢。如果你輸入一個值,那麼它將用於比較。