2015-06-30 27 views
1

是否有一種有效的方法來獲取一個列表(最好是一個數組,一個ResultSet會做)到SELECT很多行。用單一條件選擇數據的有效方法

例如:

Connection con = DriverManager.getConnection(host,username,password); 
String sql = "SELECT * FROM table_name WHERE food = ? AND expiration > ?"; 
PreparedStatement stmt = con.prepareStatement(sql); 

使用上面的代碼,我想從一個沒有過期的給定陣列得到的所有食物。

String[] foodList = {"banana","apple","orange",...} 

其中過期日期是一個固定日期(可以說3天前)。然而,我擁有的方式是String和PreparedStatement在for循環中循環數組中的食物數量以單獨檢查過期日期。這會在我分別執行每個操作後創建大量的ResultSet

+1

你是否需要單獨的每個結果?如果沒有,爲什麼不使用** select blah blah where food IN(「+ list +」)and expiration>?** – Alp

+0

我試圖抓住所有沒有過期的食物@eckes –

+0

@alp我其實沒有需要他們單獨和我會考慮使用'中' 編輯:肖恩明亮向我展示瞭如何在這種情況下使用'in' –

回答

2

大多數SQL數據庫支持IN (list)表達。這大致相當於提供一個或表達:

SELECT id FROM table WHERE food IN ('Apple', 'Banana') AND exp < ? 

類似於

SELECT id FROM table WHERE (food = 'Apple' or food = 'Banana') AND exp < ? 

在這兩種情況下,一些能夠RDBMS優化它。

但首先,您可以在IN中指定的列表項數量或可在語句中使用的字符數量受到限制。所以如果你的列表可以變長,你需要準備好運行多個語句。

其次,你不能*設置一個數組作爲PreparedStatement的參數,並期望它與IN協同工作。

不幸的是,在普通的JDBC中,你只能連接一個字符串。這是令人不悅的,但沒有好的選擇(除非你想做一些事情,比如把食物列表作爲單個列表並使用「instring」表達式)。

確保添加儘可能多的?(但是不要太多),爲您期望的參數,然後將它們設置在IN

String[] foods = ...; 
int remain = foods.length; 
int start = 0; 
while(remain > 0) 
{ if (remain >= 100) 
    executeBatch(foods, start, 100); start+=100; remain-=100; 
    else if (remain >= 30) 
    executeBatch(foods, start, 30); start+=30; remain-=30; 
    else { 
    executeBatch(foods, start, 1); start+=1; remain-=1; 
    } 
} 


void executeBatch(String[] f, int off, int len) 
{ 
    StringBuilder sqlBuf = StringBuilder("... IN("); 
    for(int i=0;i<len;i++) { 
     sqlBuf.append((i!=0)?",?":"?"); 
    } 
    String sql = sqlBuf.append(") AND exp < ?").toString(); 
    PreparedStatement ps = c.prepareStatement(sql); 
    for(int i=0;i<foods.length;i++) 
     ps.setString(i+1, foods[i+off]); 
    ps.setTimestamp(foods.length+1, now); 
    .... 
} 

這避免了產生了很多不同的SQL語句進行編譯。 (只有100,30或1?))。您可以對OR情況使用相同的邏輯。

*不要與ARRAY數據庫類型混淆。

+0

高超的方法...但我有一個疑問..爲什麼你打破上述邏輯到100,30,1,而不是直接從0到food.lenght()...你可以請求解釋這一點?.. –

+0

如果您有大量不同長度的批處理,那麼每個批處理都將是一個新的SQL語句,從而導致在Oracle側難以解析。如果你只有一些選擇的參數 - 數字(在我的例子中是3),它們將被緩存和軟解析。所以你降低了SQL區域的污染。如果你很少這麼做,或者方差很小(或不變),這並不重要。 – eckes

2

可能不是最優雅的解決方案,你不會得到從準備好的聲明任何性能優勢(但你會得到的參數綁定):

StringBuilder sql = new StringBuilder("SELECT * FROM table_name WHERE expiration > ? AND food IN ("); 

for (int i = 0; i < foodList.length; i++) { 
    if (i > 0) { 
     sql.append(','); 
    } 
    sql.append('?'); 
} 

sql.append(")"); 

Connection con = DriverManager.getConnection(host, username, password); 
PreparedStatement stmt = con.prepareStatement(sql.toString()); 

stmt.setDate(1, expirationDate); 
for (int i = 0; i < foodList.length; i++) { 
    stmt.setString(i + 2, foodList[i]); 
} 

ResultSet rs = stmt.executeQuery(); 

/* ... Do Stuff ... */ 
相關問題