报表是ERP系统的特色之一,也是很重要的组成部分。报表既有相当简单的grid展示,也有复杂的、动态的grid展示。这里分享一下一种动态报表的设计,既是对这几天被虐的一个总结,也希望得到诸位大神的指点。代码之路之长之险,让人爱恨不已,一个人搞是万万头疼滴,还是需集思广益啊。

(一)页面效果

废话不多说,先看看最后的页面效果,如下图:

SQL 2014 reporting services制作报表教程 sql报表设计_数据

(二)大致需求

1、这是薪酬管理的一个报表,需展示公司某年度的工资发放统计表,这里的工资包括:基本工资、绩效工资、各种奖金补助。

2、每个月的发放项目不固定,可能某个月只发了其中的几项,也可能所有项目都已发放。但是表格展示要求:某个月发了多少项目就展示这几个项目的数据。

3、对于一部分项目(比如某种奖金),不是人人都会发放,所以对于具体某人来说,发放了的项目就展示数据,没发放就不展示数据。

(三)设计难点

1、因为每月具体发放哪些项目是不会事先知道,而且每个会可能都会发放一些,因此表头就无法固定显示,而是动态的,具体要展示什么,需去数据库表中查询数据才知。

2、最后的数据需按表头所示展示对应的数据,以此sql查询数据也是一大难题,因为列不固定,也就意味着sql里查询的列也就不固定了。

(四)我的折腾

1、系统中的数据表模型

数据库中跟此相关的有如下几张表:

(1)人员信息表 hr_j_emp_info

(2)工资表主表 hr_j_paysheet_main,工资表明细表 HR_J_PAYSHEET_DETAIL

(3)绩效表主表 hr_j_performance_main, 绩效表明细表 hr_c_emp_performance

(4)奖金表主表 HR_BONUS_MAIN, 奖金表明细表 HR_BONUS_DETAIL (【注】因为奖金项目是动态维护的,所以奖金表主表 HR_BONUS_MAIN中可能会存在多个奖金项目)

2、分析

(1)首先,我要确定展示那些列。所以,要确定每个月发放了哪些项目。这个可通过查询表数据获得。

1 SELECT t.month,
 2        w.company_code as wcode,
 3        p.company_code as pcode,
 4        wm_concat( || '-' || i.item_name) as bs,
 5        (COUNT(w.company_code) + COUNT(p.company_code)) / decode(COUNT(i.item_name), 0, 1, COUNT(i.item_name)) + COUNT(i.item_name) as total
 6   FROM (SELECT to_char(add_months(to_date('2014-01', 'YYYY-MM'), rownum - 1), 'YYYY-MM') MONTH
 7           FROM dual
 8         CONNECT BY rownum <= 12) t, -- 月份表
 9        hr_j_paysheet_main w, -- 工资表
10        hr_j_performance_main p, -- 绩效表
11        hr_bonus_main b, -- 奖金表
12        hr_bonus_info i
13  WHERE (+) = b.bonus_id
14    AND w.month(+) = t.month
15    AND w.is_use(+) = 'Y'
16    AND w.workflow_status(+) = 'END'
17    AND p.month(+) = t.month
18    AND p.is_use(+) = 'Y'
19    AND p.workflow_status(+) = 'END'
20    AND b.month(+) = t.month
21    AND b.is_use(+) = 'Y'
22    AND b.workflow_status(+) = 'END'
23    AND w.company_code(+) = '0101'
24    AND p.company_code(+) = '0101'
25    AND b.company_id(+) = 3
26  GROUP BY t.month,
27           w.company_code,
28           p.company_code
29  ORDER BY t.month

查询结果如下:

SQL 2014 reporting services制作报表教程 sql报表设计_sql_02

上图中,第2列 wcode 表示工资表,有数据即表示这个月有数据,即发放了工资;

           第3列 pcode 表示绩效表,有数据即表示这个月有数据,即发放了绩效;

           第4列 bs      表示奖金表,有数据即表示这个月有数据,即发放了奖金;

       【注】注意看这一列数据,以第9行为例,“ 20-B奖,21-C奖,1-A奖 ”,这里是把9月发放的所有奖金合在一起了,以逗号分隔,表示发放了A奖、B奖、C奖3类,前面的数字为奖金对应的ID。

           第5列 total   表示这个月发放项目总数(也即表格中这个月下所需展示的列数)

如此,我们就能得到所需查询的月份跟发放的项目信息了。进行下一步。

(2)下面就开始查数据了,这里说两种方式

(i)分开查询。依据上面查询的结果,去循环查询每个月(有数据的月份)的数据,最后拼接成我们所需要的list。

SQL 2014 reporting services制作报表教程 sql报表设计_sql_03

SQL 2014 reporting services制作报表教程 sql报表设计_页面效果_04

1         for (int i =0; i < list.size(); i++) {
 2             // Object[] : [月份,工资,绩效,奖金,发放项目总数]
 3             Object[] o = list.get(i);
 4             if (Integer.parseInt(o[4].toString()) > 0) {
 5                 
 6                 sqlSelect = "";
 7                 sqlFrom = "";
 8                 sqlWhere = "";
 9                 sql = "";
10                 
11                 length += Integer.parseInt(o[4].toString());
12                 
13                 
14                 if (o[1] != null) {
15                     colModel += ", {\n" +
16                                 "      header : '每月工资',\n" + 
17                                 "      dataIndex : 'w_" + (i+1) + "',\n" + 
18                                 "      width : 100,\n" + 
19                                 "      align : 'center'\n" + 
20                                 "  }" ;
21                     
22                     sqlSelect += "                w.add_total,\n" ;
23                     sqlFrom   += "       (SELECT t.emp_id,\n" + 
24                                  "               t.add_total\n" + 
25                                  "          FROM hr_j_paysheet_detail t\n" + 
26                                  "         WHERE t.is_use = 'Y'\n" + 
27                                  "           AND t.company_code = '" + companyCode + "'\n" + 
28                                  "           AND t.month = '" + o[0].toString() + "') w,\n" ;
29                     sqlWhere  += "   AND w.emp_id(+) = e.emp_id\n" ;
30                 }
31                 if (o[2] != null) {
32                     colModel += ", {\n" +
33                                 "      header : '月度绩效工资',\n" + 
34                                 "      dataIndex : 'p_" + (i+1) +"',\n" + 
35                                 "      width : 100,\n" + 
36                                 "      align : 'center'\n" + 
37                                 "  }" ;
38                     
39                     sqlSelect += "                p.should_money,\n" ;
40                     sqlFrom   += "       (SELECT t.emp_code,\n" + 
41                                  "               t.should_money\n" + 
42                                  "          FROM hr_c_emp_performance t\n" + 
43                                  "         WHERE t.is_use = 'Y'\n" + 
44                                  "           AND t.dept_code = '" + companyCode + "'\n" + 
45                                  "           AND t.month = '" + o[0].toString() + "') p,\n" ;
46                     sqlWhere  += "   AND p.emp_code(+) = e.emp_code\n" ;
47                 }
48                 if (!"-".equals(o[3].toString())) {
49                     bonuses = o[3].toString().split(",");
50                     for (String b : bonuses) {
51                         bonus = b.split("-");
52                         colModel += ", {\n" +
53                                     "      header : '" + bonus[1] + "',\n" + 
54                                     "      dataIndex : 'b_" + (i+1) + "_" + bonus[0] + "',\n" + 
55                                     "      width : 100,\n" + 
56                                     "      align : 'center'\n" + 
57                                     "  }" ;
58                         
59                         sqlSelect += "                b" + bonus[0] + ".pay_amount as b" + bonus[0] + "_pay ,\n" ;
60                         sqlFrom   += "       (SELECT t.emp_id,\n" + 
61                                      "               t.pay_amount\n" + 
62                                      "          FROM hr_bonus_detail t,\n" + 
63                                      "               hr_bonus_main   m\n" + 
64                                      "         WHERE t.is_use = 'Y'\n" + 
65                                      "           AND t.main_id = \n" + 
66                                      "           AND m.company_id = " + companyId + "\n" + 
67                                      "           AND m.month = '" + o[0].toString() + "'\n" + 
68                                      "           AND m.bonus_id = " + bonus[0].toString() + ") b" + bonus[0] + ",\n" ;
69                         sqlWhere  += "   AND b" + bonus[0] + ".emp_id(+) = e.emp_id\n" ;
70                     }
71                 }
72                 
73                 sql =   "SELECT DISTINCT e.dept_id,\n" +
74                         "                e.emp_id,\n" + 
75                         "                e.chs_name,\n" + 
76                         sqlSelect +
77                         "                '-' AS n\n" + 
78                         "  FROM\n" + 
79                         sqlFrom +
80                         "       hr_j_emp_info e\n" + 
81                         " WHERE 1 > 0\n" + 
82                         sqlWhere +
83                         "   AND e.enterprise_code = '" + companyCode + "'\n" + 
84                         "   AND e.is_use = 'Y'\n" + 
85                         "   AND e.emp_state = 'U'\n" + 
86                         " ORDER BY e.dept_id,\n" + 
87                         "          e.emp_id";
88             
89                 plist = bll.queryByNativeSQL(sql);
90                 pList.add(plist);
91             }
92         }

View Code