Java JDBC:查询中的多列

Java JDBC:查询中的多列,java,sql,oracle,jdbc,jdbctemplate,Java,Sql,Oracle,Jdbc,Jdbctemplate,我有以下疑问: SELECT date, userId, value FROM tbl_table WHERE date = to_date(:date, 'YYYY-MM-DD') AND userId = :userId 它允许请求如下单个值: MapSqlParameterSource args = new MapSqlParameterSource(); args.addValue("date", date, Types.VARCHAR); args.addV

我有以下疑问:

SELECT
  date, userId, value
FROM
  tbl_table
WHERE
  date = to_date(:date, 'YYYY-MM-DD')
    AND
  userId = :userId
它允许请求如下单个值:

MapSqlParameterSource args = new MapSqlParameterSource();

args.addValue("date", date, Types.VARCHAR);
args.addValue("userId", userId, Types.VARCHAR);

SqlRowSet rowSet = jdbcTemplate.queryForRowSet(SQL_SELECT, args);

jdbcTemplate.queryForRowSet(SQL_SELECT_MARKET_VALUE, args);
create type DateUserPair as object (dt date, userid integer);
create type DateUserPairs as table of DateUserPair;
....
SELECT
  date, userId, value
FROM
  tbl_table src,
  table(cast :filter as DateUserPairs) flt
WHERE
  src.date = flt.dt and
  src.userId = flt.userId;
这完全可以,但速度非常慢,以防您必须查询多个日期/用户ID对的值


我想使用multicolumn IN子句对其进行优化,但如何通过JDBC处理多列列表(或者更好的问题:是否可以使用JDBC)?

Oracle支持“IN”谓词中的多列:

SELECT
  date, userId, value
FROM
  tbl_table
WHERE
  (date, userId) IN ((to_date(:date1, 'YYYY-MM-DD'), :userId1), (to_date(:date2, 'YYYY-MM-DD'), :userId2))

然而,JDBC并没有提供对语句内参数的良好支持——您必须使用或使用所述的一些解决方法来构建查询,如果您想要向JDBC传递一个日期/用户ID对列表,或一个日期列表和用户ID列表,我想这是行不通的

Oracle中一个可能的解决方法是使用。你应该:

  -- DDL for the workaround
  CREATE GLOBAL TEMPORARY TABLE admin_work_area
    (d DATE,
     userId VARCHAR2(10))
  ON COMMIT DELETE ROWS;
...
-- Start of query method pseudo-code
...
-- You should be able to JDBC-batch these for better performance
INSERT INTO temp_multicolumn_filter (d, userId) VALUES (date1, userId1);
INSERT INTO temp_multicolumn_filter (d, userId) VALUES (date2, userId2);
...
-- Query using temp_multicolumn_filter
SELECT date, userId, value
FROM tbl_table
WHERE
(date, userId) in (select d, userId from temp_multicolumn_filter);
...
-- End of query method pseudo-code
由于临时表在COMMIT DELETE行上有
,每个事务将只看到自己的日期/用户ID对。请记住,如果在同一事务中多次使用临时表,则可能需要在使用它之前清除它

更新:

另一个选项是使用“构建”查询中的表:

-- DDL for this workaround
CREATE TYPE date_userid_pair AS OBJECT (
    d DATE,
    userId VARCHAR2(10));

CREATE TYPE date_userid_dataset IS TABLE OF date_userid_pair;

CREATE FUNCTION decode_date_userid_pairs(dates in varchar2, userIds in varchar2) 
RETURN date_userid_dataset PIPELINED IS    
    result_row date_userid_pair;
BEGIN  
    WHILE there are more "rows" in the parameters LOOP
        result_row.d := -- Decode next date from dates
        result_row.userId := -- Decode next userId from userIds

        PIPE ROW(result_row);
    END LOOP;
END;

// Start of query method pseudo-code
...
// This is Java code: encodeList encodes a List of elements into a String.
encodedDates = encodeList(listOfDates);
encodedUserIds = encodeList(listOfUserIds);
...
// Query using temp_multicolumn_filter
SELECT date, userId, value
FROM tbl_table
WHERE
(date, userId) in (
    select date, userId 
    from TABLE(decode_date_userid_pair(:encodedDates, :encodedUserIds));
...
// End of query method pseudo-code

但是,这更麻烦,如果您没有创建临时表的权限,那么您可能也没有创建类型(您甚至可能没有创建函数权限)。

这取决于详细信息。如果用户/日期过滤器是非常持久的(应该多次使用用户),那么临时表将是最佳选择。您可以填充它一次,可以编辑它,并且可以多次使用它而无需重新加载

如果您需要相当多的对,我建议您使用表类型。应该是这样的:

MapSqlParameterSource args = new MapSqlParameterSource();

args.addValue("date", date, Types.VARCHAR);
args.addValue("userId", userId, Types.VARCHAR);

SqlRowSet rowSet = jdbcTemplate.queryForRowSet(SQL_SELECT, args);

jdbcTemplate.queryForRowSet(SQL_SELECT_MARKET_VALUE, args);
create type DateUserPair as object (dt date, userid integer);
create type DateUserPairs as table of DateUserPair;
....
SELECT
  date, userId, value
FROM
  tbl_table src,
  table(cast :filter as DateUserPairs) flt
WHERE
  src.date = flt.dt and
  src.userId = flt.userId;
如果过滤器很小,那么按((?,?),(?,?),…)中的(日期,用户ID)进行过滤将是简单而聪明的

顺便说一句,你的方法

  date = to_date(:date, 'YYYY-MM-DD')
这不是很好的练习。这种转换应该由客户端完成,而不是由服务器完成。使用

  date = :date

并将其指定为日期。

其中((?,?),(?,?),(?,?),(?,?)
将在普通JDBC中工作。我不知道它是否能与JdbcTemplate和命名参数一起工作though@a_horse_with_no_name这是一个大问题:JDBC模板将列表参数本身扩展为通配符。我使用动态创建的查询来解决这个问题。对我来说够快了。是的,够快了。这里的问题更多地与JDBC-ORA桥有关,当参数的数量未知时。不相关,但是:
to_date()
似乎不必要,如果你传递的是真实的日期(你应该这样做)@a_horse_没有名字,不仅不必要,而且可能有害,由于使用默认的日期格式会触发隐式的日期到字符串转换,这是一个很好的解决方案,但创建临时表超出了我的权限,而且由于官僚作风的原因,我只能选择解决方法。日期转换是特意在服务器上完成的。否则的话,很多地方都可能发生时区混乱。