2011-10-10 163 views
0

我通過java jdbc連接到Microsoft sql server數據庫並出現了一個非常奇怪的問題。每當我在where子句的查詢中使用佔位符參數(?),然後執行preparedStatement.setString(..)方法時,我的簡單查詢需要4800到5800毫秒的任何時間才能運行。當我在查詢本身內部對where子句進行硬編碼時,運行需要1到36毫秒。這對我來說沒有意義,因爲我認爲使用佔位符應該更快......preparedStatement.setString比硬編碼參數長5秒以上

該表確實有很多行(800萬左右),但是我傳入的參數是隻有幾個字符,我只傳入2個參數,該語句總是返回1(或0)行,它返回的數據並不是很大。桌上的索引不起作用。爲什麼這麼大的時差? 5-6秒對於這樣的查詢是非常長的時間。 where子句中的兩列都是string-type(varchar(20)),所以在數據庫端沒有隱式類型轉換(我知道)。

我已經嘗試過不同版本的sqljdbc4.jar驅動程序,但它的功能相同。

package testdbconnection; 

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 

public class Main { 

    public static void main(String[] args) throws SQLException 
    { 
     Connection con = getConnection(); 

     PreparedStatement statement = con.prepareStatement("select col1 from tableName where username = ? and password = ?"); 

     statement.setString(1, "UName"); 
     statement.setString(2, "PWord"); 

     long start = System.currentTimeMillis(); 

     ResultSet rs = statement.executeQuery(); 

     long stop = System.currentTimeMillis(); 

     System.out.println("took: " + (stop - start)); 

     rs.close(); 
     con.close(); 

    }// end main 

    private static Connection getConnection() 
    { 
     Connection connection = null; 

     try { 
      // Load the JDBC driver 
      String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; 
      Class.forName(driverName); 

      // Create a connection to the database 
      String url = "jdbc:sqlserver://DBSERVERNAME;databaseName=DBNAME;"; 
      String username = "dbUname"; 
      String password = "dbPword"; 

      connection = DriverManager.getConnection(url, username, password); 
     } 
     catch (ClassNotFoundException e) 
     { 
      e.printStackTrace(); 
     } catch (SQLException e) 
     { 
      e.printStackTrace(); 
     } 

     return connection; 
    }// end getConnection() 

} 

輸出: 運行: 了:4891 BUILD SUCCESSFUL(總時間:5秒)

現在,如果我這樣做:

package testdbconnection; 

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 

public class Main { 

    public static void main(String[] args) throws SQLException 
    { 
     Connection con = getConnection(); 

     PreparedStatement statement = con.prepareStatement("select col1 from tableName where username = 'UName' and password = 'PWord'"); 

     long start = System.currentTimeMillis(); 

     ResultSet rs = statement.executeQuery(); 

     long stop = System.currentTimeMillis(); 

     System.out.println("took: " + (stop - start)); 

     rs.close(); 
     con.close(); 

    }// end main 

    private static Connection getConnection() 
    { 
     Connection connection = null; 

     try { 
      // Load the JDBC driver 
      String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; 
      Class.forName(driverName); 

      // Create a connection to the database 
      String url = "jdbc:sqlserver://DBSERVERNAME;databaseName=DBNAME;"; 
      String username = "dbUname"; 
      String password = "dbPword"; 

      connection = DriverManager.getConnection(url, username, password); 
     } 
     catch (ClassNotFoundException e) 
     { 
      e.printStackTrace(); 
     } catch (SQLException e) 
     { 
      e.printStackTrace(); 
     } 

     return connection; 
    }// end getConnection() 

} 

輸出: 運行: 了:32 BUILD SUCCESSFUL(總時間:2秒)

回答

2

一般來說,運行完全硬編碼的語句將比等效的參數化語句更快。原因與數據庫的執行計劃有關。當您從一開始就提供所有信息時,數據庫可以執行優化,並根據您提供的確切數據選擇較短的路徑。當您對語句進行參數化時,它只能執行那些對可能插入的值有幫助的優化。

當您需要運行許多類似的查詢並希望避免每次準備語句的開銷時,參數化語句可能會有所幫助,但對於大型數據集上的單個查詢(如您的用例),硬編碼查詢會更好。

+0

在這種情況下,你會建議什麼來縮短時間並仍然使用參數化查詢?我不願意使用硬編碼的查詢,因爲where子句中的東西來自用戶輸入,而不使用參數會使應用程序更容易受到sql注入的影響。 5-6秒對於這樣一個小的查詢來說是一個很大的時間。甚至沒有任何連接。有沒有其他的事情可以在java端完成以提高性能? – Creature

+1

一個可能的解決方案是使用硬編碼查詢,如果您可以輕鬆驗證輸入是否安全(僅限字母數字和空白),並保留參數化查詢以用於更多可疑輸入。這隻有在你預期的「正常」輸入很容易識別和確定時纔有意義。 – nonVirtualThunk

+0

是的,看起來我必須這樣做。謝謝! – Creature