JDBC:多列IN查询

时间:2015-01-16 08:27:13

标签: 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.addValue("userId", userId, Types.VARCHAR);

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

jdbcTemplate.queryForRowSet(SQL_SELECT_MARKET_VALUE, args);

这是完全正常的,但如果您需要查询许多 date / userId 对的值,则极其缓慢。

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

3 个答案:

答案 0 :(得分:1)

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并没有提供对语句内参数的良好支持 - 您必须使用StringBuilder构建查询或使用描述here

的一些解决方法

答案 1 :(得分:0)

如果你想要的是传递JDBC的日期/用户ID对列表,或者日期列表和userIds列表,我认为它将不起作用。

Oracle中可能的解决方法是使用global temporary table with ON COMMIT DELETE ROWS。你的意思是:

  -- 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

由于临时表具有ON COMMIT DELETE ROWS,因此每个事务只会看到自己的date / userId对。请记住,如果您在同一个事务中多次使用临时表,则可能需要在使用它之前将其清除。

<强>更新

另一种选择是使用PIPELINED PL/SQL function在查询中“构建”您的表:

-- 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

但是这更加hacky,如果你没有创建临时表的权限,那么你可能也没有CREATE TYPE(你甚至可能没有CREATE FUNCTION权限)。

答案 2 :(得分:0)

这取决于细节。如果用户/日期过滤器非常持久(应该是用户不止一次),临时表将是最佳决策。你可以填一次,你可以编辑它,你可以多次使用它而无需重新加载。

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

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;

如果过滤器很小,在((?,?),(?,?),......)中按(date,userId)过滤将简单明了。

顺便说一句,你的方法

  date = to_date(:date, 'YYYY-MM-DD')

不是好习惯。此类转换应由客户端完成,而不是由服务器完成。使用

  date = :date

并将其指定为日期。