有这样一个原始表
产品 月份 卖出金额
JBuilder 1 10
JBuilder 2 10
JBuilder 3 10
Delphi 1 10
Delphi 2 10
Delphi 3 10
C++ Builder 1 10
C++ Builder 2 10
C++ Builder 3 10
要统计生成这样的一个报表
产品 一月 二月 三月
C++ Builder 10 10 10
JBuilder 10 10 10
Delphi 10 10 10
一定要注意的月份是不固定的即可能出现大于三个或者小于三个的月份列这是根据原始表中的信息变化的。
总体的思路是这样的,用Oracle的decode函数这样实现:
select 产品,
sum(decode(月份,1,卖出金额,0)) as 一月,
sum(decode(月份,2,卖出金额,0)) as 二月,
...
from ...
order by
由于列是不固定的,所以可以通过动态的拼凑sum(decode(....)..)..来完成。即先统计出月份的个数,然后在拼凑select语句。
我在java中做了一个类,提供一个实现这样功能的方法,代码如下:
/**
*
* @param tableName 要统计的表或者视图的名称
* @param abscissaCol 横坐标列
* @param ordinateCol 纵坐标列
* @param valueCol 值列
* @param strWhere 附加的Where条件
* @return 查询到的结果集,以ResultObj对象形式返回
*/
public static ResultObj getCrossReportData(String tableName,
String abscissaCol,
String ordinateCol,
String valueCol,
String strWhere) {
String strSQL = "select distinct " + ordinateCol + " from " + tableName +
" " + strWhere;
System.out.println("第一次生成的SQL:" + strSQL);
DBOperate dbOperate = new DBOperate();
ResultObj ordinateColData = dbOperate.Query(strSQL);
System.out.println("返回的行数:" + ordinateColData.getRows());
System.out.println("操作数据库的状态:" + ordinateColData.getStatus());
if (ordinateColData.getStatus() != 0) { //没有查询结果或出现异常
return null;
}
strSQL = "select " + abscissaCol;
for (int i = 1; i < ordinateColData.getRows(); i++) {
String ordvalue = ordinateColData.getCell(0, i); //得到纵向的值
strSQL += ",sum(decode(" + ordinateCol + ",'" + ordvalue + "'," +
valueCol + ",0)) as " + "A" + ordvalue.replace(' ','_'); //多加一个A,要不然数字不能作为列的别名,把空格用'_'替换要不然会截断SQL语句出现找不到from的错误
}
strSQL += " from " + tableName + " " + strWhere + " group by " +
abscissaCol;
System.out.println("生成的SQL:" + strSQL);
return dbOperate.Query(strSQL);
}
}
在这里要进行两次与数据库的交互,以后可能把它改写到存储过程中,让存储过程返回cursor形式,然后在jdbc中对存储过程的out参数类型设置为oracle的cursor类型(没有试过,只是看过资料)。
通过这个方法,只要传入表名,横纵坐标列和值列,还有where条件就搞定了。
在做任务的时候列的数目有一回达到了71列然后SQL语句被截断了,我还以为Oracle对SQL语句的长度有限制呢,后来查出来是列的别名中有空格的原因所以在Java的方法中做了处理。
如上,交叉报表的问题基本上是解决了。
产品 月份 卖出金额
JBuilder 1 10
JBuilder 2 10
JBuilder 3 10
Delphi 1 10
Delphi 2 10
Delphi 3 10
C++ Builder 1 10
C++ Builder 2 10
C++ Builder 3 10
要统计生成这样的一个报表
产品 一月 二月 三月
C++ Builder 10 10 10
JBuilder 10 10 10
Delphi 10 10 10
一定要注意的月份是不固定的即可能出现大于三个或者小于三个的月份列这是根据原始表中的信息变化的。
总体的思路是这样的,用Oracle的decode函数这样实现:
select 产品,
sum(decode(月份,1,卖出金额,0)) as 一月,
sum(decode(月份,2,卖出金额,0)) as 二月,
...
from ...
order by
由于列是不固定的,所以可以通过动态的拼凑sum(decode(....)..)..来完成。即先统计出月份的个数,然后在拼凑select语句。
我在java中做了一个类,提供一个实现这样功能的方法,代码如下:
/**
*
* @param tableName 要统计的表或者视图的名称
* @param abscissaCol 横坐标列
* @param ordinateCol 纵坐标列
* @param valueCol 值列
* @param strWhere 附加的Where条件
* @return 查询到的结果集,以ResultObj对象形式返回
*/
public static ResultObj getCrossReportData(String tableName,
String abscissaCol,
String ordinateCol,
String valueCol,
String strWhere) {
String strSQL = "select distinct " + ordinateCol + " from " + tableName +
" " + strWhere;
System.out.println("第一次生成的SQL:" + strSQL);
DBOperate dbOperate = new DBOperate();
ResultObj ordinateColData = dbOperate.Query(strSQL);
System.out.println("返回的行数:" + ordinateColData.getRows());
System.out.println("操作数据库的状态:" + ordinateColData.getStatus());
if (ordinateColData.getStatus() != 0) { //没有查询结果或出现异常
return null;
}
strSQL = "select " + abscissaCol;
for (int i = 1; i < ordinateColData.getRows(); i++) {
String ordvalue = ordinateColData.getCell(0, i); //得到纵向的值
strSQL += ",sum(decode(" + ordinateCol + ",'" + ordvalue + "'," +
valueCol + ",0)) as " + "A" + ordvalue.replace(' ','_'); //多加一个A,要不然数字不能作为列的别名,把空格用'_'替换要不然会截断SQL语句出现找不到from的错误
}
strSQL += " from " + tableName + " " + strWhere + " group by " +
abscissaCol;
System.out.println("生成的SQL:" + strSQL);
return dbOperate.Query(strSQL);
}
}
在这里要进行两次与数据库的交互,以后可能把它改写到存储过程中,让存储过程返回cursor形式,然后在jdbc中对存储过程的out参数类型设置为oracle的cursor类型(没有试过,只是看过资料)。
通过这个方法,只要传入表名,横纵坐标列和值列,还有where条件就搞定了。
在做任务的时候列的数目有一回达到了71列然后SQL语句被截断了,我还以为Oracle对SQL语句的长度有限制呢,后来查出来是列的别名中有空格的原因所以在Java的方法中做了处理。
如上,交叉报表的问题基本上是解决了。
回复Comments
{commenttime}{commentauthor}
{CommentUrl}
{commentcontent}