前段时间接到很多报表的工作,现拿出一些小例子与大家分享。
用户需求:
运营商管理员:
1. 以合作伙伴为维度对用户订购进行统计分析:
a. 可对某一个合作伙伴进行分析,查看该合作伙伴下产品的用户订购在某个时间段内的发展趋势,需实现折线图、柱状图。
b. 可对所有合作伙伴进行分析,对比在某段时间内所有合作伙伴下产品的用户订购发展量,查看在某段时间内所有合作伙伴下产品的用户订购发展量在订购总量中所占比例,需实现柱状图、饼状图。
2. 以产品为维度对用户订购进行统计分析:
a. 可对某一个产品进行分析,查看该产品的用户订购在某个时间段内的发展趋势,需实现折线图、柱状图。
b. 可对所有产品进行分析,对比在某段时间内所有产品的用户订购发展量,查看在某段时间内所有产品的用户订购发展量在订购总量中所占比例,需实现柱状图、饼状图。
合作伙伴管理员:
1. 以产品为维度对用户订购进行统计分析:
a. 可对本公司下某一个产品进行分析,查看该产品的用户订购在某个时间段内的发展趋势,需实现折线图、柱状图。
b. 可对本公司下所有产品进行分析,对比在某段时间内所有产品的用户订购发展量,查看在某段时间内所有产品的用户订购发展量在订购总量中所占比例,需实现柱状图、饼状图。
时间需支持按年、按月统计。
根据用户需求,我们可以开始进行实现。
实现步骤:
这里是用一个例子实现用户需求,没有用到系统内的表,系统内的表有很多例子中不需要的字段,所以根据系统内表结构新建。
1. 数据库数据表设计:
合作伙伴表(CPInfo)
产品信息表(ProductInfo):
用户订购表(UserOrderInfo):
因为是移动的系统,所以用户订购是通过手机。
2. 统计分析表建立:
由于现实系统数据量比较大,统计的数据也不是实时的,所以统计分析不直接在原始数据表上进行,这样我们就需要建立一个专门用来存放统计分析数据的表。
用户订购统计分析表(UserOrderStat):
a. StatType字段为报表类型,这里定义为:CP-按CP公司统计数据,Product-按产品统计数据
b. TimeType字段为时间类型,这里定义为:Year-按年统计数据,Month-按月统计数据
3. 统计分析存储过程建立:
按年统计存储过程:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SP_UserOrderStatByYear]') AND type in (N'P', N'PC')) DROP PROCEDURE [dbo].[SP_UserOrderStatByYear] GO --按年统计用户订购 CREATE PROCEDURE SP_UserOrderStatByYear @BeginYear INT, --开始年份 @EndYear INT --结束年份 AS BEGIN DECLARE @Temp_BeginYear INT DECLARE @Temp_EndYear INT DECLARE @BeginTime NVARCHAR(16) DECLARE @EndTime NVARCHAR(16) SET @Temp_BeginYear = @BeginYear SET @Temp_EndYear = @EndYear + 1 --判断结束年份是否为当前时间年份或大于当前时间年份 IF @EndYear >= YEAR(GETDATE()) SET @Temp_EndYear = YEAR(GETDATE()) SET @BeginTime = CAST(@Temp_BeginYear AS NVARCHAR(8)) + '-1-1' SET @EndTime = CAST(@Temp_EndYear AS NVARCHAR(8)) + '-1-1' --添加年统计数据 INSERT INTO UserOrderStat ( StatType, TimeType, [Year], CPCode, CPChName, ProductCode, ProductName, OrderCount ) --按CP公司统计 SELECT 'CP', 'Year', YEAR(uoi.OrderTime), uoi.CPCode, MAX(c.CPChName), '', '', COUNT(0) FROM UserOrderInfo uoi JOIN CPInfo c ON uoi.CPCode = c.CPCode JOIN ProductInfo p ON uoi.ProductCode = p.ProductCode WHERE uoi.Status = 1 AND uoi.OrderTime > @BeginTime AND uoi.OrderTime < @EndTime AND NOT EXISTS (SELECT 0 FROM UserOrderStat uos WHERE uos.CPCode = uoi.CPCode AND uos.Year = YEAR(uoi.OrderTime)) GROUP BY uoi.CPCode, YEAR(uoi.OrderTime) UNION --按产品统计 SELECT 'Product', 'Year', YEAR(uoi.OrderTime), MAX(uoi.CPCode), MAX(c.CPChName), uoi.ProductCode, MAX(p.ProductName), COUNT(0) FROM UserOrderInfo uoi JOIN CPInfo c ON uoi.CPCode = c.CPCode JOIN ProductInfo p ON uoi.ProductCode = p.ProductCode WHERE uoi.Status = 1 AND uoi.OrderTime > @BeginTime AND uoi.OrderTime < @EndTime AND NOT EXISTS (SELECT 0 FROM UserOrderStat uos WHERE uos.CPCode = uoi.CPCode AND uos.ProductCode = uoi.ProductCode AND uos.Year = YEAR(uoi.OrderTime)) GROUP BY uoi.ProductCode, YEAR(uoi.OrderTime) END GO
按月统计存储过程:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SP_UserOrderStatByMonth]') AND type in (N'P', N'PC')) DROP PROCEDURE [dbo].[SP_UserOrderStatByMonth] GO --按月统计用户订购 CREATE PROCEDURE SP_UserOrderStatByMonth @BeginYear INT, --开始年份 @BeginMonth INT, --开始月份 @EndYear INT, --结束年份 @EndMonth INT --结束月份 AS BEGIN DECLARE @Temp_BeginYear INT DECLARE @Temp_BeginMonth INT DECLARE @Temp_EndYear INT DECLARE @Temp_EndMonth INT DECLARE @BeginTime NVARCHAR(16) DECLARE @EndTime NVARCHAR(16) SET @Temp_BeginYear = @BeginYear SET @Temp_BeginMonth = @BeginMonth SET @Temp_EndYear = @EndYear SET @Temp_EndMonth = @EndMonth + 1 --判断结束年份是否大于当前时间年份,当前月份是否为1月或者判断结束年份是否等于当前时间年份,结束月份是否大于当前时间月份 IF @EndYear > YEAR(GETDATE()) OR (@EndYear = YEAR(GETDATE()) AND @EndMonth > MONTH(GETDATE())) BEGIN SET @Temp_EndYear = YEAR(GETDATE()) SET @Temp_EndMonth = MONTH(GETDATE()) END SET @BeginTime = CAST(@Temp_BeginYear AS NVARCHAR(8)) + '-' + CAST(@BeginMonth AS NVARCHAR(8)) + '-1' SET @EndTime = CAST(@Temp_EndYear AS NVARCHAR(8)) + '-' + CAST(@EndMonth AS NVARCHAR(8)) + '-1' --添加月统计数据 INSERT INTO UserOrderStat ( StatType, TimeType, [Year], [Month], CPCode, CPChName, ProductCode, ProductName, OrderCount ) --按CP公司统计 SELECT 'CP', 'Month', YEAR(uoi.OrderTime), MONTH(uoi.OrderTime), uoi.CPCode, MAX(c.CPChName), '', '', COUNT(0) FROM UserOrderInfo uoi JOIN CPInfo c ON uoi.CPCode = c.CPCode JOIN ProductInfo p ON uoi.ProductCode = p.ProductCode WHERE uoi.Status = 1 AND uoi.OrderTime > @BeginTime AND uoi.OrderTime < @EndTime AND NOT EXISTS (SELECT 0 FROM UserOrderStat uos WHERE uos.CPCode = uoi.CPCode AND uos.Year = YEAR(uoi.OrderTime) AND uos.Month = MONTH(uoi.OrderTime)) GROUP BY uoi.CPCode, YEAR(uoi.OrderTime), MONTH(uoi.OrderTime) UNION --按产品统计 SELECT 'Product', 'Month', YEAR(uoi.OrderTime), MONTH(uoi.OrderTime), MAX(uoi.CPCode), MAX(c.CPChName), uoi.ProductCode, MAX(p.ProductName), COUNT(0) FROM UserOrderInfo uoi JOIN CPInfo c ON uoi.CPCode = c.CPCode JOIN ProductInfo p ON uoi.ProductCode = p.ProductCode WHERE uoi.Status = 1 AND uoi.OrderTime > @BeginTime AND uoi.OrderTime < @EndTime AND NOT EXISTS (SELECT 0 FROM UserOrderStat uos WHERE uos.CPCode = uoi.CPCode AND uos.ProductCode = uoi.ProductCode AND uos.Year = YEAR(uoi.OrderTime) AND uos.Month = MONTH(uoi.OrderTime)) GROUP BY uoi.ProductCode, YEAR(uoi.OrderTime), MONTH(uoi.OrderTime) END GO
4. 新建数据库作业,用来定时执行年统计存储过程和月统计存储过程,一个用来执行月统计存储过程,计划为每月的1日零点执行。至此数据准备工作完成,现在开始着手设计报表。
在例子中用三层架构的方式实现,分为UI层、业务逻辑层、数据访问层。
5. 新建用户统计报表工程解决方案
统计分析数据查询存储过程:
--统计查询存储过程 CREATE PROCEDURE [dbo].[SP_UserOrderStatQuery] @StatType NVARCHAR(32), @TimeType NVARCHAR(32), @BeginYear INT, @EndYear INT, @BeginMonth INT, @EndMonth INT, @CPCode NVARCHAR(32), @CPName NVARCHAR(256), @ProductCode NVARCHAR(32), @ProductName NVARCHAR(256) AS BEGIN --查询字段 DECLARE @Field NVARCHAR(512) --查询条件 DECLARE @Where NVARCHAR(512) --按范围查询时间字段 DECLARE @Time NVARCHAR(256) --分组 DECLARE @Group NVARCHAR(256) --排序 DECLARE @Order NVARCHAR(256) --设置查询字段 SET @Field = N'MAX([SeqNo]) AS SeqNo,MAX([StatType]) AS StatType,MAX([TimeType]) AS TimeType, MAX([Year]) AS Year,MAX([Month]) AS Month,MAX([CPCode]) AS CPCode,MAX([CPChName]) AS CPChName, MAX([ProductCode]) AS ProductCode,MAX([ProductName]) AS ProductName,MAX([OrderCount]) AS OrderCount' SET @Time = '' --如果时间条件都未选择,则查询总时间段 IF @BeginYear = -1 AND @EndYear = -1 AND @BeginMonth = -1 AND @EndMonth = -1 SET @Time = ',''总时间段'' AS [Time]' --如果年为时间条件,并且开始年与结束年相等 IF @BeginYear <> -1 AND @EndYear <> -1 AND @BeginYear = @EndYear AND @BeginMonth = -1 AND @EndMonth = -1 SET @Time = ',''' + CAST(@BeginYear AS NVARCHAR(8)) + '年'' AS [Time]' --如果年为时间条件,并且开始年与结束年不相等 IF @BeginYear <> -1 AND @EndYear <> -1 AND @BeginYear <> @EndYear AND @BeginMonth = -1 AND @EndMonth = -1 SET @Time = ',''' + CAST(@BeginYear AS NVARCHAR(8)) + '年 - ' + CAST(@EndYear AS NVARCHAR(8)) + '年'' AS [Time]' --如果年和月为时间条件,并且开始年与结束年相等,开始月与结束月相等 IF @BeginYear <> -1 AND @EndYear <> -1 AND @BeginYear = @EndYear AND @BeginMonth <> -1 AND @EndMonth <> -1 AND @BeginMonth = @EndMonth SET @Time = ',''' + CAST(@BeginYear AS NVARCHAR(8)) + '年' + CAST(@BeginMonth AS NVARCHAR(8)) + '月'' AS [Time]' --如果年和月为时间条件,并且开始年与结束年相等或者开始年与结束年不相等,开始月与结束月不相等 IF @BeginYear <> -1 AND @EndYear <> -1 AND @BeginMonth <> -1 AND @EndMonth <> -1 AND ((@BeginYear = @EndYear AND @BeginMonth <> @EndMonth) OR (@BeginYear <> @EndYear AND @BeginMonth <> @EndMonth)) SET @Time = ',''' + CAST(@BeginYear AS NVARCHAR(8)) + '年' + CAST(@BeginMonth AS NVARCHAR(8)) + '月 - ' + CAST(@EndYear AS NVARCHAR(8)) + '年' + CAST(@EndMonth AS NVARCHAR(8)) + '月'' AS [Time]' SET @Field = @Field + @Time SET @Where = ' WHERE 1=1 ' --如果时间类型不为按时间范围查询 IF @TimeType <> 'Area' SET @Where = @Where + ' AND TimeType = ''' + @TimeType + '''' --如果StatType不为空 IF @StatType <> '' AND @StatType IS NOT NULL SET @Where = @Where + ' AND StatType = ''' + @StatType + '''' --如果CPCode不为空 IF @CPCode <> '' AND @CPCode IS NOT NULL SET @Where = @Where + ' AND CPCode = ''' + @CPCode + '''' --如果CPName不为空 IF @CPName <> '' AND @CPName IS NOT NULL SET @Where = @Where + ' AND CPName LIKE ''%' + @CPName + '%''' --如果ProductCode不为空 IF @ProductCode <> '' AND @ProductCode IS NOT NULL SET @Where = @Where + ' AND ProductCode = ''' + @ProductCode + '''' --如果ProductName不为空 IF @ProductName <> '' AND @ProductName IS NOT NULL SET @Where = @Where + ' AND ProductName = ''%' + @ProductName + '%''' IF @BeginYear <> -1 AND @EndYear <> -1 SET @Where = @Where + ' AND ((Year >= ' + CAST(@BeginYear AS NVARCHAR(8)) + ' AND YEAR <= ' + CAST(@EndYear AS NVARCHAR(8)) + ')' IF @BeginMonth <> -1 AND @EndMonth <> -1 SET @Where = @Where + ' OR (Month >= ' + CAST(@BeginMonth AS NVARCHAR(8)) + ' AND Month <= ' + CAST(@EndMonth AS NVARCHAR(8)) + ')' IF @BeginYear <> -1 AND @EndYear <> -1 SET @Where = @Where + ')' SET @Group = ' GROUP BY ' IF @StatType = 'CP' SET @Group = @Group + 'CPCode' IF @StatType = 'Product' SET @Group = @Group + 'ProductCode' IF @TimeType = 'Year' SET @Group = @Group + ',Year' IF @TimeType = 'Month' SET @Group = @Group + ',Month' PRINT 'SELECT ' + @Field + ' FROM UserOrderStat ' + @Where + @Group EXECUTE ('SELECT ' + @Field + ' FROM UserOrderStat ' + @Where + @Group) END GO
数据访问类:UserOrderStatDAL.cs
在这里用到了企业库进行数据库操作
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.Common; using Microsoft.Practices.EnterpriseLibrary.Data; namespace DAL { public partial class UserOrderStatDAL { /// <summary> /// 获取用户订购统计分析数据 /// </summary> /// <param name="statType">统计类型:CP-按CP统计,Product-按产品统计</param> /// <param name="timeType">时间类型:Year-按年统计,Month-按月统计,Area-按时间范围统计</param> /// <param name="beginYear">统计开始年份</param> /// <param name="endYear">统计结束年份</param> /// <param name="beginMonth">统计开始月份</param> /// <param name="endMonth">统计结束月份</param> /// <param name="cpCode">CP编码</param> /// <param name="cpName">CP名称</param> /// <param name="productCode">产品编码</param> /// <param name="productName">产品名称</param> /// <returns></returns> public static DataSet GetUserOrderStatData(string statType, string timeType, int beginYear, int endYear, int beginMonth, int endMonth, string cpCode, string cpName, string productCode, string productName) { //创建Database对象 Database db = DatabaseFactory.CreateDatabase("ReportDBConnection"); //创建DbCommand对象 DbCommand dbCommand = db.GetStoredProcCommand("SP_UserOrderStatQuery"); //添加输入输出参数 db.AddInParameter(dbCommand, "@StatType", DbType.String, statType); db.AddInParameter(dbCommand, "@TimeType", DbType.String, timeType); db.AddInParameter(dbCommand, "@BeginYear", DbType.Int32, beginYear); db.AddInParameter(dbCommand, "@EndYear", DbType.Int32, endYear); db.AddInParameter(dbCommand, "@BeginMonth", DbType.Int32, beginMonth); db.AddInParameter(dbCommand, "@EndMonth", DbType.Int32, endMonth); db.AddInParameter(dbCommand, "@CPCode", DbType.String, cpCode); db.AddInParameter(dbCommand, "@CPName", DbType.String, cpName); db.AddInParameter(dbCommand, "@ProductCode", DbType.String, productCode); db.AddInParameter(dbCommand, "@ProductName", DbType.String, productName); return db.ExecuteDataSet(dbCommand); } } }
业务逻辑层:UserOrderStatBLL.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DAL; using System.Data; namespace BLL { public class UserOrderStatBLL { /// <summary> /// 获取用户订购统计分析数据 /// </summary> /// <param name="statType">统计类型:CP-按CP统计,Product-按产品统计</param> /// <param name="timeType">时间类型:Year-按年统计,Month-按月统计,Area-按时间范围统计</param> /// <param name="beginYear">统计开始年份</param> /// <param name="endYear">统计结束年份</param> /// <param name="beginMonth">统计开始月份</param> /// <param name="endMonth">统计结束月份</param> /// <param name="cpCode">CP编码</param> /// <param name="cpName">CP名称</param> /// <param name="productCode">产品编码</param> /// <param name="productName">产品名称</param> /// <returns></returns> public static DataSet GetUserOrderStatData(string statType, string timeType, int beginYear, int endYear, int beginMonth, int endMonth, string cpCode, string cpName, string productCode, string productName) { try { //调用DAL层方法 return UserOrderStatDAL.GetUserOrderStatData(statType, timeType, beginYear, endYear, beginMonth, endMonth, cpCode, cpName, productCode, productName); } catch (Exception ex) { //在此处记录异常日志,并抛出异常 throw ex; } } } }
在这个例子里,这一层没有起到任何作用,但是在实际的系统里这一层是必须的。
6. 新建数据集:
在UI层中新建一个数据集
在数据集中添加一个DataTable
7. 新建报表文件:
在本例子中使用rdlc报表
报表文件新建后,从工具栏中拖一个饼状图到设计器
然后从网站数据源中将相应字段拖到饼状图中,如下:
数据字段放置报表统计字段,类别字段决定报表统计字段按什么进行分类,而序列字段是在分类的基础上再进行细分。
在这例子里只演示按CP统计用户订购量,所以只需要将订购量拖到数据字段,将CP编码或CP名称拖到类别字段便可。
可以为报表添加输入参数,在报表菜单下报表参数内进行添加,在本例子中我添加了一个title参数和一个bottom参数。
到此一个报表文件便设计完成。
8. 新建报表展示页面:
UserOrderStat.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserOrderStat.aspx.cs" Inherits="UserOrderReportPortal.UserOrderStat" %> <%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <rsweb:ReportViewer ID="rptView" runat="server" SizeToReportContent="true" Height="500px" Width="100%" BackColor="Linen"> </rsweb:ReportViewer> </div> </form> </body> </html>
在页面上我只放了一个ReportViewer控件,大家在做例子的时候可以加上条件输入控件。
UserOrderStat.aspx.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.Reporting.WebForms; using BLL; using System.Data; namespace UserOrderReportPortal { public partial class UserOrderStat : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { //设置报表参数值 ReportParameter[] parameters = new ReportParameter[2]; parameters[0] = new ReportParameter("bottom", "Terence"); parameters[1] = new ReportParameter("title", "用户订购统计报表"); //调用业务逻辑层方法获取数据 DataSet ds = UserOrderStatBLL.GetUserOrderStatData("CP", "Year", 2006, 2009, -1 , -1, "", "", "", ""); //设置报表路径 rptView.LocalReport.ReportPath = "RdlcFile\\UserOrderStat_CP_Year.rdlc"; rptView.LocalReport.DataSources.Clear(); //绑定报表 rptView.LocalReport.DataSources.Add(new ReportDataSource("UserOrderStatDataSource_UserOrderStat", ds.Tables[0])); rptView.LocalReport.SetParameters(parameters); rptView.DataBind(); } } } }
Web.config
需在<compilation>节点下添加
<buildProviders> <add extension=".rdlc" type="Microsoft.Reporting.RdlBuildProvider, Microsoft.ReportViewer.Common, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </buildProviders>
在<HttpHandlers>节点下添加
<add verb="*" path="Reserved.ReportViewerWebControl.axd" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
最终运行效果图:
该效果图显示的就是2006年至2009年所有CP公司的订购发展量比例对比。
更改查询条件
//调用业务逻辑层方法获取数据 DataSet ds = UserOrderStatBLL.GetUserOrderStatData("Product", "Month", 2006, 2009, 1 , 12, "CP001", "", "", "");
更改报表图为柱状图,更改类别字段为产品,在序列字段中增加年份、月份
效果图如下:
该效果图显示的就是测试CP1下产品在2006年1月至2009年12月中订购发展量趋势分析。
大家在实践的时候可以把饼状图换成其它的图,然后把统计条件进行更改,会有不同的效果。
希望我的文章对大家有帮助。
源代码下载:下载