2010-05-18 58 views

回答

3

我用盡可能多的?構建SQL字符串,因爲我有值,以尋找解決此。

SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?) 

首先我搜索了我可以傳入語句的數組類型,但所有JDBC數組類型都是供應商特定的。所以我留在多個?。我能想到的

+1

這就是我們現在正在做的,但我希望有一個統一的方式來做到這一點沒有自定義SQL ... – Uri 2010-05-18 21:30:14

+0

另外,如果它是像甲骨文一樣,它將不得不重新解析大部分每個語句。 – orbfish 2010-07-15 14:46:41

-1

一種方法是使用java.sql.PreparedStatement中和位評委的索具

的PreparedStatement preparedStmt = conn.prepareStatement( 「SELECT * FROM MYTABLE WHERE MYCOL在()?」);

...然後...

preparedStmt.setString(1,[您stringged PARAMS]);

http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html

+6

這不會起作用,因爲它可能會創建一個查詢,比如'... WHERE MYCOL IN('2,3,5,6')'這不是您正在嘗試執行的操作。 – Progman 2010-05-18 21:50:53

1

據我所知,有在JDBC處理集合作爲參數沒有標準的支持。如果你能通過一個List並且將被擴展,那將是非常棒的。

Spring的JDBC訪問支持將集合作爲參數傳遞。你可以看看這是如何安全地進行編碼的靈感。

Auto-expanding collections as JDBC parameters

(文章首先討論了休眠,然後繼續討論JDBC)

36

有確實JDBC做到這一點沒有直接的方法。 部分 JDBC驅動程序似乎支持IN子句中的PreparedStatement#setArray()。我只是不確定哪些是。

你可以只使用一個輔助方法與String#join()Collections#nCopies()來生成IN條款和其它輔助方法來與PreparedStatement#setObject()設置在循環中的所有值的佔位符。

public static String preparePlaceHolders(int length) { 
    return String.join(",", Collections.nCopies(length, "?")); 
} 

public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException { 
    for (int i = 0; i < values.length; i++) { 
     preparedStatement.setObject(i + 1, values[i]); 
    } 
} 

這裏是你如何使用它:

private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)"; 

public List<Entity> find(Set<Long> ids) throws SQLException { 
    List<Entity> entities = new ArrayList<Entity>(); 
    String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size())); 

    try (
     Connection connection = dataSource.getConnection(); 
     PreparedStatement statement = connection.prepareStatement(sql); 
    ) { 
     setValues(statement, ids.toArray()); 

     try (ResultSet resultSet = statement.executeQuery()) { 
      while (resultSet.next()) { 
       entities.add(map(resultSet)); 
      } 
     } 
    } 

    return entities; 
} 

private static Entity map(ResultSet resultSet) throws SQLException { 
    Enitity entity = new Entity(); 
    entity.setId(resultSet.getLong("id")); 
    entity.setName(resultSet.getString("name")); 
    entity.setValue(resultSet.getInt("value")); 
    return entity; 
} 

注意,某些數據庫有IN子句中值的允許量的限制。例如,Oracle對1000個項目有這個限制。

+1

這種方法會導致SQL注入嗎? – 2011-09-22 07:04:47

+3

@Kaylan:沒有單一代碼行將用戶控制的輸入raw添加到SQL查詢字符串中。所以絕對沒有SQL注入風險的手段。 – BalusC 2011-09-22 12:51:56

+0

好。感謝您的澄清 – 2011-10-23 14:50:34

0

看我的試用和它的成功,據說列表大小有潛在的限制。 列表l =數組。asList(new Integer [] {12496,12497,12498,12499}); Map param = Collections.singletonMap(「goodsid」,l);

NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource()); 
    String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)"; 
    List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class); 
0

sormula使得這個簡單的(見Example 4):

ArrayList<Integer> partNumbers = new ArrayList<Integer>(); 
partNumbers.add(999); 
partNumbers.add(777); 
partNumbers.add(1234); 

// set up 
Database database = new Database(getConnection()); 
Table<Inventory> inventoryTable = database.getTable(Inventory.class); 

// select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..." 
for (Inventory inventory: inventoryTable. 
    selectAllWhere("partNumberIn", partNumbers))  
{ 
    System.out.println(inventory.getPartNumber()); 
} 
12

由於無人接聽的大IN子句的情況下(超過100)我會扔我解決了這個問題,其很適合用於JDBC。簡而言之,我將IN替換爲tmp表格上的INNER JOIN

我所做的是製作我稱之爲批處理ID表,並根據RDBMS我可以做一個tmp表或內存表。

該表有兩列。一列帶有來自IN子句的ID,另一列帶有我在飛行中生成的批次ID。

SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ? 

之前您選擇您的推IDS到表中給定的批次ID。 然後,您只需將您的原始查詢IN子句替換爲您的ID表上的INNER JOIN匹配WHERE batch_id等於您當前的批次。完成之後,爲您批量刪除條目。

+2

+1這對於大型數據集非常有效,並且不會破壞數據庫 – jasonk 2012-07-08 11:31:43

+0

嗯,是不是半連接('IN'或'EXISTS'謂詞)可能會勝過內部連接? – 2017-03-30 20:51:06

+0

@LukasEder YMMV與我的SQL僞代碼。一如既往的測試/基準。這是一個有趣的想法。說到這一點,我應該去看看我們的實際SQL是什麼。 – 2017-03-31 12:50:35

7

執行此操作的標準方法是(如果您使用Spring JDBC)是使用org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate類。

使用此類,可以將List定義爲SQL參數,並使用NamedParameterJdbcTemplate來替換命名參數。例如:

public List<MyObject> getDatabaseObjects(List<String> params) { 
    NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource); 
    String sql = "select * from my_table where my_col in (:params)"; 
    List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper); 
    return result; 
} 
0

我們可以使用不同的替代方法。

  1. 執行單查詢 - 慢,不推薦
  2. 使用存儲過程 - 具體
  3. 創建的PreparedStatement查詢動態數據庫 - 良好的性能,但緩存的鬆散的利益和需要重新編譯
  4. 使用PreparedStatement的查詢NULL - 我認爲這是一個優化性能的好方法。

查看有關這些here的更多詳情。

2

得到的答覆從docs.spring(19.7.3)

SQL標準允許用於選擇基於包括值的變量列表中的表達式的行。一個典型的例子是select * from T_ACTOR,其中id在(1,2,3)中。這個變量列表並不直接支持JDBC標準的預處理語句;你不能聲明可變數目的佔位符。您需要準備好所需數量的佔位符時需要多種變體,或者一旦知道需要多少佔位符,就需要動態生成SQL字符串。 NamedParameterJdbcTemplate和JdbcTemplate中提供的named參數支持採用後一種方法。將這些值作爲基本對象的java.util.List傳遞。此列表將用於插入所需的佔位符並在語句執行過程中傳遞值。

希望這可以幫助你。

相關問題