參數化SQL語句


避免SQL注入的方法有兩種:
一是所有的SQL語句都存放在存儲過程中,這樣不但可以避免SQL注入,還能提高一些性能,並且存儲過程可以由專門的數據庫管理員(DBA)編寫和集中管理,不過這種做法有時候針對相同的幾個表有不同條件的查詢,SQL語句可能不同,這樣就會編寫大量的存儲過程,所以有人提出了第二種方案:參數化SQL語句。例如我們在本篇中創建的表UserInfo中查找所有女性用戶,那么通常情況下我們的SQL語句可能是這樣:

1
select  * from  UserInfo where  sex=0

在參數化SQL語句中我們將數值以參數化的形式提供,對於上面的查詢,我們用參數化SQL語句表示為: 

1
select  * from  UserInfo where  sex=@sex

再對代碼中對這個SQL語句中的參數進行賦值,假如我們要查找UserInfo表中所有年齡大於30歲的男性用戶,這個參數化SQL語句可以這么寫:

1
select  * from  UserInfo where  sex=@sex and age>@age

下面是執行這個查詢並且將查詢結果集以DataTable的方式返回的代碼: 

1
2
3
4
5
6
7
8
9
10
11
12
13
//實例化Connection對象
SqlConnection connection = new  SqlConnection( "server=localhost;database=pubs;uid=sa;pwd=''" );
//實例化Command對象
SqlCommand command = new  SqlCommand( "select * from UserInfo where sex=@sex and age>@age" , connection);
//第一種添加查詢參數的例子
command.Parameters.AddWithValue( "@sex" , true );
//第二種添加查詢參數的例子
SqlParameter parameter = new  SqlParameter( "@age" , SqlDbType.Int); //注意UserInfo表里age字段是int類型的
parameter.Value = 30;
command.Parameters.Add(parameter); //添加參數
//實例化DataAdapter
SqlDataAdapter adapter = new  SqlDataAdapter(command);
DataTable data = new  DataTable();

上面的代碼是訪問SQL Server數據庫的代碼。如果本文中提到的數據分別在Access、MySQL、Oracle數據庫,那么對應的參數化SQL語句及參數分別如下:

數據庫 Access MySQL Oracle
 SQL語句 select * from UserInfo 
where sex=? and age>?
select * from UserInfo 
where sex=?sex and age>?age
select * from UserInfo 
where sex=:sex and age>:age
參數 OleDbParameter MySqlParameter OracleParameter
實例化參數 OleDbParameter p=new OleDbParameter(“?”, OleDbType. Boolean); MySqlParameter p=new MySqlParameter(“?sex”, MySqlDbType.Bit); OracleParameter p=new OracleParameter(“:sex”, OracleType.Byte);
賦值 p.Value=true; p.Value=1; p.Value=1;

      通過上面的實例代碼我們可以看出盡管SQL語句大體相似,但是在不同數據庫的特點,可能參數化SQL語句不同,例如在Access中參數化SQL語句是在參數直接以“?”作為參數名,在SQL Server中是參數有“@”前綴,在MySQL中是參數有“?”前綴,在Oracle中參數以“:”為前綴。 
注意:因為在Access中參數名都是“?”,所以給參數賦值一定要按照列順序賦值,否則就有可能執行出錯。

 

Command對象傳參效率測試

在.net平台,普通的insert語句有兩種寫法,不帶參數insert into test(c1,c2) values(var1,var2)和帶參數insert into test(c1,c2) values(:c1,:c2),它們的執行效率如何呢?
做了個試驗,代碼如下:(數據庫是oracle)

   public partial class WebForm1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //test1();
            test2();
        }
        private void test1()
        {
            OracleConnection con = new OracleConnection();
            con.ConnectionString = "Data Source=oracl;User Id=xxx;Password=xxx;Persist Security Info=True;";
            System.Random r = new Random((int)System.DateTime.Now.Ticks);
            string strCommand = "insert into test(c1,c2) values({0},{1})";
            OracleCommand com = new OracleCommand();
            com.Connection = con;
            con.Open();
            DateTime dt = DateTime.Now;
            Label1.Text = "不傳參:"+DateTime.Now.ToLongTimeString();
            for (int i = 0; i < 50000; i++)
            {
 
                com.CommandText = string.Format(strCommand, r.Next(), r.Next());
                com.ExecuteNonQuery();
            }
            com.CommandText = "truncate table test";
            com.ExecuteNonQuery();
            con.Close();
            Label2.Text = DateTime.Now.ToLongTimeString();
        }
        private void test2()
        {
            OracleConnection con = new OracleConnection();
 
            con.ConnectionString = "Data Source=bocodb;User Id=hljyd;Password=hljyd;Persist Security Info=True;";
            System.Random r = new Random((int)System.DateTime.Now.Ticks);
            string strCommand = "insert into test(c1,c2) values(:c1,:c2)";
            OracleCommand com = new OracleCommand();
            com.Parameters.Add(":c1", OracleType.Number);
            com.Parameters.Add(":c2", OracleType.Number);
            com.CommandText = strCommand;
            com.Connection = con;
            con.Open();
            Label1.Text = "傳參:"+DateTime.Now.ToLongTimeString();
            for (int i = 0; i < 50000; i++)
            {
                com.Parameters[":c1"].Value = r.Next();
                com.Parameters[":c2"].Value = r.Next();
                 
                com.ExecuteNonQuery();
            }
            com.Parameters.Clear();
            com.CommandText = "truncate table test";
            com.ExecuteNonQuery();
            con.Close();
            Label2.Text = DateTime.Now.ToLongTimeString();
        }
    }

 

執行結果:
10000記錄:
不傳參數?5:46:19 15:46:34 15秒
傳參數:?5:50:51 15:51:01 10秒

50000記錄:
不傳參數  16:09:03 16:10:24 81秒
傳參數::16:15:43 16:16:36 53秒
這只是2個參數的情況,如果參數很多會不會影響更大呢?

10000記錄,7個參數:
不傳參數:17:11:01 17:11:18 17秒
傳參數:17:13:46 17:13:59 13秒
50000記錄:7個參數:
不傳參數:17:19:02 17:20:25 1分23秒
傳參數:17:15:09 17:16:10 1分1秒

需要相差不大,但是向command對象傳遞參數既可以避免sql注入問題,也可以提高性能;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM