1. 程式人生 > >動態SQL和繫結變數

動態SQL和繫結變數

說動態SQL之前先來說下靜態SQL

靜態SQL語句

語句中主變數的個數與資料型別在預編譯時都是確定的,我們稱這類嵌入式SQL語句為靜態SQL語句。

與之相對應的就是動態SQL

動態SQL方法允許在程式執行過程中臨時“組裝”SQL語句。

那麼他們之間的區別是什麼呢?

靜態sql的執行計劃(DB2稱存取路徑)是在執行前就確定好的

動態sql的執行計劃(DB2稱存取路徑)是在執行時動態生成的。由於是在執行時動態生成執行計劃,因此生成的執行計劃(DB2稱存取路徑)相對更優,但考慮到生成執行計劃(DB2稱存取路徑)的開銷,有可能應用程式的執行時間相對會比靜態sql長些 。

繫結變數 

在 SQL 語句中,繫結變數是一個佔位符。例如,為了查詢員工號為 123 的員工的資訊,可以查詢:

1 ) select * from emp where empno=123;

另外,也可以查詢:

2 ) select * from emp where empno=:empno 。

     那麼每次查詢都是一個新查詢,即在資料庫共享池中以前沒有過的查詢。每次查詢必須經過分析、限定(名稱解析)、安全檢查、優化等等,簡單地說,執行的每條語句在每次執行時都將必須經過編譯。

在第二個查詢 2 )中使用了繫結變數 :empno ,它的值在查詢執行時提供。查詢經過一次編譯後,查詢方案將儲存在共享池中,可以用來檢索和重用。在效能和可伸縮性方面,這兩者的差異是巨大的,甚至是驚人的

       從上所述,很明顯看出,分析一個帶有硬編碼量的語句將比重用一條已分析過的查詢方案花費更長的時間和消耗更多的資源,不明顯的是前者將減少系統所能支援的使用者數量。很明顯,部分原因是由於增加資源消耗量,但更主要的因素是在解析 sql 語句的過程中對共享池中鎖存器( latch )的爭搶

       通過使用繫結變數,應用程式提交的相似的 sql 語句只需要解析一次,就可以重複使用,這非常有效,這也是Oracle 資料庫要求使用的工作方式。不僅使用較少的資源,而且可以減少鎖存( latch )時間,降低鎖存( latch )次數,這將提高應用系統性能,並且大大提高可伸縮性。

     在IBATS中,採用的是動態SQL加繫結變數的方式。

     IBATS中是根據傳遞過來的變數是否空來拼接SQL的,這就是動態SQL。

     而傳遞來的引數是根據繫結變數來執行的。

     比如下面的例子,我要查詢使用者資訊。

     在前臺頁面輸入使用者名稱,zhangsan 

     傳到IBATS後,執行SQL,在資料庫檢視剛剛執行的SQL

     檢視方法如下:

Sql程式碼
  1. select  sql_text  from  v$sql  where  sql_text  like   'select  t.username ,t.password, t.sex ,t.mobile from users t%'   

     注意:在ORACLE中,要檢視v$sql 這個檢視,得較高的許可權,我是用DBA許可權進去檢視的。

     看到這樣的語句:

Sql程式碼
  1. select   t.username ,t. password , t.sex ,t.mobile  from  users t              where               t.username = :1    

     如果再查lisi的使用者資訊,在資料庫看執行的SQL。發現還是上面那條SQL。

    所以,繫結變數的好處就是每次都執行的同一條SQL,只是輸入的變數值不同。

    下面直接在資料量中執行下面的兩個SQL:

Sql程式碼
  1. select   t.username ,t. password , t.sex ,t.mobile  from  users t   where   t.username =  'zhangsan' ;  
  2. select   t.username ,t. password , t.sex ,t.mobile  from  users t   where   t.username =  'lisi' ;  

    再看資料庫中執行的SQL,發現是下面的結果:

     

   可以看到,不使用繫結變數時,是執行的不同的SQL。

總結:ibats採用的是動態SQL+繫結變數的方式。 動態SQL在執行時編譯,所以執行計劃更優,但相對於靜態編譯的靜態SQL或者儲存過程更耗效能。

 繫結變數可以重複利用相似的SQL,使效率更高。

--------------------------------------------------------------------------------

附一段jdbc訪問資料庫時繫結和不繫結變數的測試例子:

       import java.sql.*;

       import oracle.jdbc.driver.*;

  class ConOra {
  public static void main(String args[] ) throws SQLException{
  DriverManager.registerDriver(neworacle.jdbc.driver.OracleDriver());
  Connection conn =DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.152:1521:whx","system","lukewhx");
  PreparedStatement stmt ;
  ResultSet rset ;
  String v_sql;

  /*不繫結
  for (int i =1;i<=1000;i++){
  v_sql="select object_name from objects where object_id="+i;
  stmt =conn.prepareStatement(v_sql);
  rset=stmt.executeQuery();
  stmt.close();
  } 
  */

      //繫結

  for (int i =1 ;i<=1000;i++ ) {
v_sql = "select object_name from objects where object_id= :x ";
  stmt=conn.prepareStatement(v_sql);
stmt.setString(1,Integer.toString(i));
  rset = stmt.executeQuery();
  stmt.close();
  } 

  System.out.println("Execute OK");
  }
  }