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_没有名字,不仅不必要,而且可能有害,由于使用默认的日期格式会触发隐式的日期到字符串转换,这是一个很好的解决方案,但创建临时表超出了我的权限,而且由于官僚作风的原因,我只能选择解决方法。日期转换是特意在服务器上完成的。否则的话,很多地方都可能发生时区混乱。