使用多列的参数化 IN 子句
Parameterized IN clause using multiple columns
我有一个沿着这些线的查询,我试图通过比较元组来过滤结果集(比如 SQL multiple columns in IN clause):
select *
from mytable
where (key, value) in (values
('key1', 'value1'),
('key2', 'value2'),
...
);
这是有效的语法,在我的 PostgreSQL 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 和参数列表的情况下完成此操作?
不幸的是,没有任何简单的方法可以将嵌套集合绑定变量绑定到 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
绑定变量
您所要做的就是传递一个数组列表,其中每个数组包含一个键和值,如下所示:
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
如果您的解决方案无法正常工作,您也可以只连接键和值。
也许 JDBC 使用这种更基本的语法时问题较少:
select *
from mytable
where (key||value) in (
('key1value1'),
('key2value2'),
...
);
为此,您需要先将 Java 映射转换为键和值也串联在一起的列表。
这可能不是查询的问题,可能是您没有任何 hstore created/installed。
我建议按照以下步骤来调试您的问题:
- 尝试一个非常简单的查询,不带任何参数。
- 如果遇到同样的问题,请查看如何创建扩展:http://www.postgresql.org/docs/9.1/static/sql-createextension.html
- 否则,如果查询正确执行,请尝试使用简单参数(with
=?
)
- 最后,尝试命名查询。类似于:
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?
我有一个沿着这些线的查询,我试图通过比较元组来过滤结果集(比如 SQL multiple columns in IN clause):
select *
from mytable
where (key, value) in (values
('key1', 'value1'),
('key2', 'value2'),
...
);
这是有效的语法,在我的 PostgreSQL 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 和参数列表的情况下完成此操作?
不幸的是,没有任何简单的方法可以将嵌套集合绑定变量绑定到 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
绑定变量
您所要做的就是传递一个数组列表,其中每个数组包含一个键和值,如下所示:
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
如果您的解决方案无法正常工作,您也可以只连接键和值。 也许 JDBC 使用这种更基本的语法时问题较少:
select *
from mytable
where (key||value) in (
('key1value1'),
('key2value2'),
...
);
为此,您需要先将 Java 映射转换为键和值也串联在一起的列表。
这可能不是查询的问题,可能是您没有任何 hstore created/installed。
我建议按照以下步骤来调试您的问题:
- 尝试一个非常简单的查询,不带任何参数。
- 如果遇到同样的问题,请查看如何创建扩展:http://www.postgresql.org/docs/9.1/static/sql-createextension.html
- 否则,如果查询正确执行,请尝试使用简单参数(with
=?
) - 最后,尝试命名查询。类似于:
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?