关于在SQL语句中是用Bind Variables的重要性就不用多说了,最近在看代码的时候发现有一条比较耗时的SQL语句居然是在Java代码中动态拼接而成的。这条SQL语句之前也进行过一些简单的重写优化工作,在with语句中特别用到hint 了/*+materialize*/。之前认为这个hint在WITH语句中没有必要使用,Oracle应该会自动创建一个临时表用于with语句结果的存储,结果居然发现加不加这个hint差别还是蛮大的,最后就加上了这个hint.
OK,回到正题上来。这个SQL需要接收的参数是一个动态的逗号分隔的字符串,放在in list中用的。很显然,如果用PL/SQL来写这个语句的话,是不能用如下的方式的....
---------------------------------------------------------------------------------
function test(p_list_of_names in varchar2)return ....
as
select *
from ...
where name in (p_list_of_names)
...
end;
--------------------------------------------------------------------------------
一般这种情况下用Pl/SQL方式的话,最简单的就是用动态SQL执行,但是很显然这个就会失去bind variables的优势,会造成SQL Hard Parse次数的增加。为了避免动态SQL, 一般我们会借助nested table来做,但是这样会造成执行计划的不准确。这个可以参见(http://www.cnblogs.com/fangwenyu/archive/2011/04/18/2020191.html).
不过貌似这个问题在Hibernate中很容易得到解决,SQL语句还是可以写成上面那样,只是在绑定参数的时候,传入一个数组就OK了。如下所示...
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
public class demo {
private String sql;
private NamedParameterJdbcTemplate jdbcTemplate;
public void setSql(final String sql) {
this.sql = sql;
}
public String getSql() {
return this.sql;
}
public List<XXX> retrieveResult(final String userId) {
Set<String> userIds = combineUserIds(userId);
Map<String, Object> params = new HashMap<String, Object>();
params.put("userIds", userIds);
List<XXX> result = jdbcTemplate.query(this.getSql(), params,
new RowMapper<XXX>() {
public XXX mapRow(ResultSet rs, int line) throws SQLException {
....
这里面的SQL语句放在了XML文件中,用Spring IoC自动初始化...
<bean id="demo"class="demo">
<property name="sql" value="
select * from xxx where user_id in (:userIds)"/>
</bean>
在运行的时候,Hibernate会把:userIds替换成一些列的"?" placeholder (根据传进来数组的元素个数),这样也就可以使用绑定变量了。