我有以下查询:
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)?
答案 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
并将其指定为日期。