java.sql.SQLException: Prepared or callable statement has more than 2000 parameter markers及解決方案


1. 問題

     最近在項目中修bug的時候,碰到這樣一個錯誤:

Caused by: java.sql.SQLException:Prepared or callable statement has more than 2000 parameter markers.    

at net.sourceforge.jtds.jdbc.SQLParser.parse(SQLParser.java:1139)    
at net.sourceforge.jtds.jdbc.SQLParser.parse(SQLParser.java:156)    
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.<init>(JtdsPreparedStatement.java:107)

Caused by: java.sql.SQLException:Prepared or callable statement has more than 2000 parameter markers.

     出錯的代碼簡化如下(list中的元素個數可能大於2000):

1 SELECT * FROM xxx x  
2          WHERE x.date <= :today AND x.id IN (:list) 

2. 收獲

     起初並不明白是什么情況,經過一番google加請教之后,有一些收獲。項目中用的JTDS的driver版本為1.2.4,數據庫為SQL Server。在該driver的源碼中找到如下一個片段(在net.sourceforge.jtds.jdbc.SQLParser類中):


     從這段代碼中可以看出,其實這並不是一個bug,而是work as designed。我們得到的另一個信息是,對於不同類型的數據庫,不同的版本之間,這種約束是有差別的,分別是255,1000,2000。這樣看來,之前的查詢語句出錯也就很好理解了,在IN子句中的參數個數多於2000是肯定會拋異常的。

3. 解決方案

     主要有兩種解決方案:一種是優化SQL語句,一種是優化查詢邏輯(即分批處理)。
     對於第一種方案,如果IN子句中的參數來自於另一張表的話,可以考慮類似如下的查詢方式:

1 SELECT * FROM xxx x
2          INNER JOIN yyy y ON x.id = y.id   
3          WHERE x.date <= :today 

     對於第二種方案,分批來查詢速度沒有第一種快,但是不用修改SQL語句。

1         List<Item> result = Lists.newArrayList();
2         List<List<String>> partitionIds = Lists.partition(ids, BATCH_SIZE);
3         for (List<String> partitionId : partitionIds) {
4             param.put("list", partitionId);
5             List<Item> resultItems = xxxService.query(param);
6             result.addAll(resultItems);
7         }
8         return result;

     這種方案的劣勢在於速度上會有損失,但是可以從Java代碼的邏輯層避免上面的問題。

     希望這些對大家有所幫助。


 

 

 


免責聲明!

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



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