2016-02-05 101 views
5

我沿着這些線路,在這裏我想篩選結果通過比較元組設置(如SQL multiple columns in IN clause)查詢:使用參數化IN子句多列

select * 
from mytable 
where (key, value) in (values 
('key1', 'value1'), 
('key2', 'value2'), 
... 
); 

這是有效的語法和工作正常,我Postgres 9.3數據庫。

我想通過Spring JDBC調用此查詢,其中in值對來自List<Map<String,String>>

這將是很好做這樣的事情:

List<Map<String, String>> valuesMap = ...; 
String sql = "select * from mytable where (key, value) in (values :valuesMap)"; 
SqlParameterSource params = new MapSqlParameterSource("valuesMap", valuesMap); 
jdbcTemplate.query(sql, params, rowMapper); 

當我嘗試,我得到:

org.postgresql.util.PSQLException: No hstore extension installed. 
    at org.postgresql.jdbc2.AbstractJdbc2Statement.setMap(AbstractJdbc2Statement.java:1707) ~[postgresql-9.3-1101-jdbc41.jar:na] 
    at org.postgresql.jdbc2.AbstractJdbc2Statement.setObject(AbstractJdbc2Statement.java:1910) ~[postgresql-9.3-1101-jdbc41.jar:na] 
    at org.postgresql.jdbc3g.AbstractJdbc3gStatement.setObject(AbstractJdbc3gStatement.java:36) ~[postgresql-9.3-1101-jdbc41.jar:na] 
    at org.postgresql.jdbc4.AbstractJdbc4Statement.setObject(AbstractJdbc4Statement.java:47) ~[postgresql-9.3-1101-jdbc41.jar:na] 
    at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:427) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:235) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:150) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.setValues(PreparedStatementCreatorFactory.java:287) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.createPreparedStatement(PreparedStatementCreatorFactory.java:244) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:623) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE] 

我看着它提到的hstore擴展。這似乎與我的問題沒有關係。

有沒有辦法在不動態構建SQL和參數列表的情況下完成此操作?

回答

2

所有你需要做的是通過陣列,每個陣列包含一個鍵和值,像這樣的列表:

HashMap<String , String > map = new HashMap<>(); 
map.put("key0", "value0"); 
map.put("key1", "value1"); 
Set<String> keys = map.keySet(); 
List<String[]> valuesMap = new ArrayList<>(); 
for(String key:keys){ 
    String[] entry = {key,map.get(key)}; 
    valuesMap.add(entry); 
} 
String sql = "select * from mytable where (key, value) in (values :valuesMap)"; 
SqlParameterSource params = new MapSqlParameterSource("valuesMap", valuesMap); 
jdbcTemplate.query(sql, params, rowMapper); 

這Spring文檔中提及:http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/jdbc.html#jdbc-in-clause

+0

這正是我一直在尋找,謝謝。事後看來,Object []比Map更有意義。 –

0

不幸的是,沒有任何簡單的方法將嵌套的集合綁定變量綁定到PostgreSQL。您可以生成以下SQL字符串,而不是

SELECT * 
FROM mytable 
WHERE (key, value) IN (
    (?, ?), 
    (?, ?), 
    ... 
); 

這是一個保持SQL字符串和變量綁定同步的工作。 你可以,但是,地圖編碼爲JSON這樣:

SELECT * 
FROM mytable 
WHERE (key, value) IN (
    SELECT 
    t->>'key', 
    t->>'value' 
    FROM json_array_elements(?) AS t(v) 
) 

例如

SELECT * 
FROM mytable 
WHERE (key, value) IN (
    SELECT 
    t->>'key', 
    t->>'value' 
    FROM json_array_elements(
    '[{"key":"key1","value":"value1"}, 
     {"key":"key2","value":"value2"}]' 
) AS t(v) 
) 

在這種情況下,你將永遠只需要一個VARCHAR綁定變量

0

如果你不能讓你的解決方案的工作,你也可以只concaten吃了鑰匙和價值。 也許JDBC少的問題,這個更基本的語法:

select * 
from mytable 
where (key||value) in (
('key1value1'), 
('key2value2'), 
... 
); 

對於這個工作,你需要先轉換你的Java地圖與串聯以及鍵和值的列表。

+0

在鍵和值之間使用分隔符可能不是一個壞主意,它不會出現在數據中。例如。 '鍵|| '###'||值「(直到它*確實出現在數據中,當然) –

-1

查詢可能不是問題,可能是您沒有創建/安裝任何hstore。

我建議以下步驟來調試您的問題:

  1. 嘗試一個非常簡單的查詢,不帶任何參數。
  2. 如果你得到了同樣的問題,檢查如何創建擴展:http://www.postgresql.org/docs/9.1/static/sql-createextension.html
  3. 否則,如果查詢正確執行,儘量使用簡單的參數(與=?
  4. 最後,嘗試命名查詢。喜歡的東西:
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(namedSql); 
List<Integer> parameters = new ArrayList<Integer>(); 
for (A a : paramBeans) 
    parameters.add(a.getId()); 
MapSqlParameterSource parameterSource = new MapSqlParameterSource(); 
parameterSource.addValue("placeholder1, parameters); 
// create SQL with ?'s 
String sql = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource); 
return sql; 

還要檢查這個討論,我覺得有用:How to execute IN() SQL queries with Spring's JDBCTemplate effectivly?