最近查到关于sql批量写入和绑定变量的问题,看到了很多好的帖子,再次进行总结
数据库在执行SQL语句时会首先解析SQL语句,解析又分为硬解析与软解析。说到硬解析和软解析,就不能不说一下Oracle对sql的处理过程。当你发出一条sql语句交付Oracle,在执行和获取结果前,Oracle对此sql将进行几个步骤的处理过程:
1、语法检查(syntax check)
检查此sql的拼写是否语法。
2、语义检查(semantic check)
诸如检查sql语句中的访问对象是否存在及该用户是否具备相应的权限。
3、对sql语句进行解析(prase)
利用内部算法对sql进行解析,生成解析树及执行计划。
4、执行sql,返回结果(execute and return)。
其中,软、硬解析就发生在第三个过程里,Oracle利用内部的hash算法来取得该sql的hash值,然后在librarycache里查找是否存在该hash值。假设存在,则将此sql与cache中的进行比较(注意此处的比较哪怕是一个字母的大小写、空格个数不一致都会认为不同)。假设“相同”,就将利用已有的解析树与执行计划,而省略了优化器的相关工作。这也就是软解析的过程;如果上面的2个假设中任有一个不成立,那么优化器都将进行创建解析树、生成执行计划的动作。这个过程就叫硬解析。由此可以看出应该极力避免硬解析,尽量使用软解析。
通过http://blog.csdn.net/duanning397/article/details/7586609 我们看出两者的差距还是很大的
那么如何进行软解析,在这里主要通过绑定变量的方法
认识绑定变量:
绑定变量是为了减少解析,比如你有个语句这样的:
select aaa,bbb from ccc where ddd=eee;
如果经常通过改变eee这个谓词赋值来查询,如下:
select aaa,bbb from ccc where ddd=fff; select aaa,bbb from ccc where ddd=ggg; select aaa,bbb from ccc where ddd=hhh;
每条语句都要被数据库解析一次,这样比较浪费资源,如果把eee换成绑定变量形式,无论ddd后面是什么值,都不需要重复解析
通过
那么如何在java中实现绑定变量,主要用两种方法,一种是?号法,比较简单粗暴,适合大部分情况
具体可以参考http://blog.csdn.net/wacthamu/article/details/7798393 这里转载一些关键内容
Java实现绑定变量的方法:
PreparedStatement pstmt = con.prepareStatement("UPDATE employees SET salay = ? WHERE id = ?"); pstmt.setBigDecimal(1, 15.00); pstmt.setInt(2, 110592); //result statmement: UPDATE employees SET salay = 15.00 WHERE id =110592 pstmt.executeQuery(); |
---|
假设要将id从1到10000的员工的工资都更新为150.00元,不使用绑定变量,则:
sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 1"); sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 2"); sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 3"); sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 4"); .... sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 10000"); |
---|
使用绑定变量,则:
PreparedStatement pstmt; for (id = 1; id < 10000; id ) { if (null == pstmt) pstmt = con.prepareStatement("UPDATE employees SET salay = ? WHERE id = ?"); pstmt.setBigDecimal(1, 150.00); pstmt.setInt(2, id); pstmt.executeQuery(); } |
---|
需要注意的是pstmt.executeQuery();是每次都打开游标,所以出国处理大数据的话,则需要在后面添加pstmt.close();
这里可以看到通过设置的方式可以让数据库认为成一条语句,这里必须按照这种方法,如果用简单的字符串拼接的方法,数据库依然认为成两条语句具体可以参考http://www.itpub.net/thread-263389-1-1.html
那么第二种方法是什么呢,细心的网友可以看到http://blog.csdn.net/wacthamu/article/details/7798393 已经提及,即使用:x的方式,其中该文章,也给出了一样的赋值方式,
其实Oracle遵循针对PL/SQL存储过程使用占位符名称匹配的原则,而针对SQL语句则采用占位符位置匹配的原则。所以上文的:x更多的是在Oracle中的本身里面,对比两者可以发现,一种是单纯的问号,一种是用变量名,当赋值的变量是重复的时候,用后者可以避免重复书写,但后者由于是变量名,也会导致书写错误,
在近期研究中发现,在hibernate中的hql语言其实也是可以占位符名称匹配,这里给出一个完整的hql的存储例子
代码语言:javascript复制<span style="color:#333333;">StringBuffer hql = new StringBuffer();
hql.append("select distinct employee from PreceptPO pre,PlanPO plan, ").append(
"EmployeeInfoPO employee,ObjectRelationPO relation ");
hql.append(" where pre.oid=plan.preceptID");
hql.append(" and pre.status='").append(PreceptStatusType.STARTUP.getCode()).append("'");
hql.append(" and plan.objectID=employee.oid ");
hql.append(" and relation.relationObjectID=:employeeID ");
hql.append(" and relation.preceptID=plan.preceptID ");
hql.append(" and relation.objectID=plan.objectID ");
hql.append(" and relation.phase=plan.phase ");
hql.append(" and (relation.performanceRole=3 or relation.performanceRole=4 )");
//员工状态
hql.append(" and ");
DAOHelper.buildOrHql(hql, "employee.employeeStatus",
StaffStatusAttribute.Normal_Employee.getStatus());
Query q = getSession().createQuery(hql.toString());
/*if(empList.size() > 0){
</span><span style="color:#ff0000;"><strong>q.setParameterList("empList", empList); </strong></span><span style="color:#333333;">
}*/
q.setLong("employeeID", appraiserID);</strong>
objectList = q.list();
List voList = new ArrayList(objectList.size());
for (Iterator iter = objectList.iterator(); iter.hasNext();) {
EmployeeInfoPO emp = (EmployeeInfoPO) iter.next();
voList.add(EmployeeBasicConvert.convert(emp));
}
return voList;
}</span>
这个时候很多人会说,既然使用了createQuery,那使用createSQLQuery可不可以用占位符名称匹配,答案是可以的,下面给出了例子
代码语言:javascript复制StringBuffer buffer = new StringBuffer();
/*
buffer.append(" select count(1) as num from TB_PMG_OBJECTRELATION orn,TB_PMG_PLAN pl ")
.append(" left join TB_PMG_EVALUATESCORE on TB_PMG_EVALUATESCORE.c_planid = pl.c_oid_plan ")
.append(" and TB_PMG_EVALUATESCORE.c_status in (2,3,5,7) ")
.append(" where pl.c_oid_plan=").append(planID)
.append(" and pl.c_objectid = orn.c_objectid ")
.append(" and orn.c_preceptid = pl.c_preceptid and orn.c_phase = pl.c_phase ")
.append(" and orn.c_level = '2' and orn.c_businessrole='1' ")
.append(" and TB_PMG_EVALUATESCORE.C_OID_EVALUATESCORE is not null");
*/
buffer.append(" SELECT COUNT(1) AS NUM FROM TB_PMG_OBJECTRELATION ORN ")
.append(" LEFT JOIN TB_PMG_EVALUATESCORE e on e.C_STATUS IN (2,3,5,7)")
.append(" AND ORN.C_PRECEPTID = e.c_preceptID ")
.append(" AND ORN.C_PHASE = e.c_phase ")
.append(" AND ORN.C_objectID = e.c_objectID ")
.append(" WHERE ")
.append(" ORN.C_OBJECTID =:objectID ")
.append(" AND ORN.C_PRECEPTID = :preceptID AND ORN.C_PHASE = :phase ")
.append(" AND ORN.C_LEVEL = '2' AND ORN.C_BUSINESSROLE='1' ")
.append(" AND e.C_OID_EVALUATESCORE IS NOT NULL ");
SQLQuery q= loadSession().createSQLQuery(buffer.toString());
q.setParameter("preceptID", preceptID);
q.setParameter("phase", phase);
q.setParameter("objectID", objectID);</strong>
q.addScalar("num", Hibernate.LONG);
List list = q.list();
其实在大型工程中,占位符名称匹配用的很多,但如果就是用单纯的sql语句怎么实现呢?,下文给出了方式http://blog.csdn.net/keyboardsun/article/details/4275594
这里需要用一个自定义函数,进行拼接赋值,这里面还对不用类型的内容进行转换,比如时间,数字等比较复杂的转换,所以这个自定义函数可以作为一个工具类
很感谢以上所有的博主,给我的帮助,在这里也专程感谢