【问题标题】:Parameterized IN clause using multiple columns使用多列的参数化 IN 子句
【发布时间】:2016-05-15 19:08:00
【问题描述】:

我有一个这样的查询,我试图通过比较元组来过滤结果集(如 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 和参数列表的情况下实现这一点?

【问题讨论】:

    标签: java postgresql spring-jdbc


    【解决方案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?

    【讨论】:

      【解决方案2】:

      如果您的解决方案无法正常工作,您也可以将键和值连接起来。 也许 JDBC 使用这种更基本的语法问题较少:

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

      为此,您需要首先将您的 Java Map 转换为 List,同时将键和值连接起来。

      【讨论】:

      • 在键和值之间使用分隔符可能不是一个坏主意,它不会出现在数据中。例如。 key || '###' || value(当然,直到它确实出现在数据中)
      【解决方案3】:

      您所要做的就是传递一个数组列表,其中每个数组都包含一个键和值,如下所示:

      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

      【讨论】:

      • 这正是我想要的,谢谢。事后看来,无论如何,Object[] 比 Map 更有意义。
      • 为了清楚起见,在代码示例中,最终列表将包含两个数组:[key1, value1] [key0, value0]
      • 如果你像我一样在 jpa 中做同样的事情,请参阅:stackoverflow.com/questions/37190664/…
      【解决方案4】:

      不幸的是,没有任何简单的方法可以将嵌套集合绑定变量绑定到 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 绑定变量

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-25
        相关资源
        最近更新 更多