在最近的项目中,碰到一小段数据库数据分析的程序,需要结合多张表联合查询或涉及到子查询,项目主要采用的java ee开发,使用了hibernate框架,由于这些表没用从实体通过hibernate直接生成,也没有外键之类的东西,于是就开始写sql,写完sql再放入程序中转为hql,然后查询。
写好的sql:中文:50 英文:1075 数字:0 中文+标点:50 中文+数字:50 。字符总数:共记1175个字符 (汉字算两个字符,数字、空格、英文字母算做一个字符),放入程序中相当难看,java中不像c#可以使用@“”将一个字符串分多行书写(或许有有种方法鄙人没发现),写作一行太长,多行使用“+”连接总感觉效率低了那么一点点心里不舒服。
最主要的原因是:后期维护实在太难。
鉴于如上几点,就将一个大的sql联合查询改为视图,将子查询也做成视图,最终可以直接查询单一视图获取需要的数据,代码中也简洁,好维护,需要修改直接改视图就行了。
但是,查询的数据是要经过动态的传入参数作查询条件的,视图依然不是最好的解决方案,所以就只能用存储过程了,因为以前从来没用过mysql的存储过程,所以一开始就没选这种方法,实在不行了,还得学,不过学起来还是很快的,很快就做好了,完成后程序代码也简洁,后期好维护,效率也可以。
上面涉及到的一些知识总结如下:
1、子查询:
SELECT H.hosCode,H.hosName,K.keshiName,N.doctornum,R.receivedcount,T.keshitime
FROM(/*统计科室医生接待人数*/
SELECT Q.keshiId,COUNT(*) AS receivedcount
FROM( /*医生和科室关系*/
SELECT D.doctorId,K.keshiId
FROM erpsinglekeshi K,erpsingledoctor D
WHERE K.keshiId=D.keshiId) as Q,erpdoctorreceivedcount R
WHERE Q.doctorId=R.doctorId
GROUP BY R.doctorId) AS R,
(/*统计科室医生在线时间*/
SELECT Q.keshiId,SUM(T.onlineTime) AS keshitime
FROM( /*医生和科室关系*/
SELECT D.doctorId,K.keshiId
FROM erpsinglekeshi K,erpsingledoctor D
WHERE K.keshiId=D.keshiId) as Q,erpdoctoronlinetime T
WHERE Q.doctorId=T.doctorId
GROUP BY T.doctorId) as T,
(/*科室人数统计*/
SELECT D.keshiId,COUNT(*) AS doctornum
FROM erpsingledoctor D
GROUP BY D.keshiId) AS N,erpsinglehospital H,erpsinglekeshi K
WHERE N.keshiId=T.keshiId AND R.keshiId=N.keshiId AND H.hospitalId=K.hospitalId AND K.keshiId=N.keshiId
2、sql转hql
sql转hql其实不难,hibernate其实是可以直接调用sql的,如果一定要将sql转为hql(好处是可以直接将实体与表对应起来),只要将表名改为实体名,字段名与实体定义相同即可。
3、视图
视图其实就是一个写好的sql,可以将需要的数据(通常是从多张表组合而来)生成一张表,但这个表是虚拟的,硬盘上并不保存这张表的数据,只会保存这个生成过程,但对于用户,查询普通表和视图是一样的,使用视图可以让用户不能直接操作表,只用作查询的时候很有用,视图创建如下(其中联合查询的几张表也是视图):
/*single_doctor*/
create view single_doctor as
SELECT H.hosCode,H.hosName,K.keshiName,N.doctornum,R.receivedcount,T.keshitime
FROM keshi_received AS R,keshi_onlinetime as T,keshi_num AS N,erpsinglehospital H,erpsinglekeshi K
WHERE N.keshiId=T.keshiId AND R.keshiId=N.keshiId AND H.hospitalId=K.hospitalId AND K.keshiId=N.keshiId
4、存储过程
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它:
#使用Navicat创建的存储过程
BEGIN
#Routine body goes here...
SELECT H.hosCode,H.hosName,K.keshiName,N.doctornum,R.receivedcount,T.keshitime,T.docType
FROM(/*统计科室医生接待人数*/
SELECT Q.keshiId,COUNT(*) AS receivedcount
FROM( /*医生和科室关系*/
SELECT D.doctorId,K.keshiId
FROM erpsinglekeshi K,erpsingledoctor D
WHERE K.keshiId=D.keshiId) as Q,erpdoctorreceivedcount R
WHERE Q.doctorId=R.doctorId AND R.currentTimestartTime
GROUP BY R.doctorId) AS R,
(/*统计科室医生在线时间*/
SELECT Q.keshiId,SUM(T.onlineTime) AS keshitime,T.docType
FROM( /*医生和科室关系*/
SELECT D.doctorId,K.keshiId
FROM erpsinglekeshi K,erpsingledoctor D
WHERE K.keshiId=D.keshiId) as Q,erpdoctoronlinetime T
WHERE Q.doctorId=T.doctorId AND T.currentTimestartTime
GROUP BY T.doctorId) as T,
(/*科室人数统计*/
SELECT D.keshiId,COUNT(*) AS doctornum
FROM erpsingledoctor D
GROUP BY D.keshiId) AS N,erpsinglehospital H,erpsinglekeshi K
WHERE N.keshiId=T.keshiId AND R.keshiId=N.keshiId AND H.hospitalId=K.hospitalId AND K.keshiId=N.keshiId AND N.doctornum
5、hibernate调用存储过程
@Autowired
protected HibernateTemplate hibernateTemplateMysql;
public List getDoctorOnlineDetails(final long startTime,final long endTime) {
return hibernateTemplateMysql.execute(new HibernateCallback() {
@SuppressWarnings("deprecation")
public Object doInHibernate(Session session) throws HibernateException,SQLException {
List doctorOnlines = new ArrayList<>();
String procdure = "{Call pro_doctor_online_detail(?,?)}";
CallableStatement cs;
cs = session.connection().prepareCall(procdure);
cs.setLong(1, startTime);
cs.setLong(2, endTime);
ResultSet rs = cs.executeQuery();
//H.hosCode,H.hosName,K.keshiName,D.doctorName,T.time,R.received
while(rs.next()){
doctorOnlines.add(new DoctorOnlineDetail(rs.getString(1),rs.getString(2),rs.getString(3),rs.getString(4),rs.getLong(5),rs.getInt(6),rs.getInt(7)));
}
return doctorOnlines;
}
});
}