标签:
导出报表,将程序中的list或者dataTable进行组织。然后通过特定的形式,显示到Excel或者word中,方便打印。
目前正在使用的方式,事先用报表设计工具,设置一个模板,然后导出报表的时候,读取模板,然后将模板中的数据进行替换。这也是最常用的一个方式。
我们公司,现在没有使用报表工具,使用的Excel。
使用Excel做报表模板,然后向Excel中写数据,进而达到一个导出报表的功能。
因为有大量的数据需要写入到Excel,标签只是一个标记。
这样就提供了两种写入Excel的方式:
方法一
逐个单元格进行数据传输。程序和Excel相互交互
方法二
将大量有规律的数据,直接传输给Excel,标签只是一个起始位置。程序和Excel交互一次。
测试两种方法时间比较
在6000条数据的测试下,
第一种方案,2min54s
第二种方案,1min22s
很显然,直接把dataTable提交给Excel比一个一个的写单元格要快的多。节省的时间,就是程序和Excel进行数据交互的过程。
导出模板设计思路:
1、写一个Excel模板,在里面写一些标签
2、替换模板中的标签,然后向里面写数据
3、对报表中的数据区域进行格式调整
模板中的行数不固定,列数不固定,所以模板大概形式如下:
然后,把数据,替换到标签中。
在使用第一种方案的时候,导出花费的时间 都在第二步 替换{T$data} 标签上。时间复杂度 O(n2)
所以在导出的数据量过大的时候,慢就算了。而且,这种方案还会报错。
“如果您具有少量的数据,则逐个单元格地传输数据是可以接受的方法。您可以灵活地将数据放到工作簿中的任何地方,并可以在运行时根据条件对单元格进行格式设置。然而,如果您具有大量需要传输到 Excel 工作簿的数据,则使用这种方法不是一个好主意。您在运行时获取的每一个Range 对象都会产生一个接口请求,这意味着数据传输速度会变得较慢。此外,Microsoft Windows 95、Microsoft Windows 98 以及 Microsoft Windows Millennium Edition (Me) 都对接口请求有 64 KB 的限制。如果您具有 64 KB 以上的接口请求,则“自动化”服务器 (Excel) 可能会停止响应,或者您可能会收到指出内存不足的错误信息。”
//读取Excel模板并打开
string execPath = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath);
pathTemplateFile = Path.Combine(Path.Combine(execPath, @"File\"), "Template.xlt");
var myExcel = new Excel.Application
{
Visible = true,
UserControl = true,
DisplayAlerts = false,
AlertBeforeOverwriting = false
};
Excel.Workbooks workbooks = myExcel.Workbooks;
Excel._Workbook workbook = workbooks.Add(pathTemplateFile);
Excel.Sheets sheets = workbook.Sheets;
第一种方案的实现:
代码实现:
((Excel.Range)worksheet.Rows[rng.Row, missing]).Copy(worksheet.Rows[curRow, missing]);
foreach (DataRow dr in tableData.Rows)
{
// 插入行并复制格式行 rng.EntireRow.Insert(Excel.XlInsertShiftDirection.xlShiftDown);
//((Excel.Range)worksheet.Rows[rng.Row, missing]).Copy(worksheet.Rows[curRow, missing]);
// 填充行数据
var curCol = rng.Column;
foreach (DataColumn dc in tableData.Columns)
{
worksheet.Cells[curRow, curCol] = dr[dc].ToString();
curCol++;
}
curRow++;
}
第二种方案:
使用Range对象的CopyfromRecordset方法,直接将dataTable放入到标签的位置(标签作为左上角的单元格)
//复制{$Cols}所在行的格式
((Excel.Range)worksheet.Rows[rng.Row, missing]).Copy(worksheet.Rows[curRow, missing]); for (int j = 0; j < dbRows; j++)
{
//然后以dataTable 的行数 插入若干行 rng.EntireRow.Insert(Excel.XlInsertShiftDirection.xlShiftDown);
}
//寻找标签
object findText = "{T$data}";
//获取标签所在位置的Range
Excel.Range objRange = worksheet.Rows.Find(find, missing, missing, missing, missing, Excel.XlSearchDirection.xlNext, missing, missing);//获取Range对象
//使用该Range对象,把数据集 直接放到标签的位置
objRange.CopyFromRecordset(rs, Type.Missing, Type.Missing);
其中,rs是ADODB.Recordset对象。
//将数据区域的dataTable转换成Recordset ADODB.Recordset rs = ConvertToRecordset(dtTable); //获取数据区域的行数 int dbRows = dtTable.Rows.Count; //获取数据区域的列数 int dbCols = dtTable.Columns.Count;
注意:CopyFromRecordset 只能与 ADORecordset 对象一起使用。使用 ADO.NET 创建的DataSet 不能与 CopyFromRecordset 方法一起使用。以下几部分中的多个示例演示了如何利用 ADO.NET 向 Excel 传输数据。
所以需要将dataTable转换为Recordset对象。
/// <summary>
/// 将Datatable转换成Recordset对象
/// </summary>
/// <param name="inTable"></param>
/// <returns></returns>
public static ADODB.Recordset ConvertToRecordset(DataTable inTable)
{
ADODB.Recordset result = new ADODB.Recordset();
result.CursorLocation = ADODB.CursorLocationEnum.adUseClient;
ADODB.Fields resultFields = result.Fields;
System.Data.DataColumnCollection inColumns = inTable.Columns;
foreach (DataColumn inColumn in inColumns)
{
resultFields.Append(inColumn.ColumnName
//, TranslateType(inColumn.DataType)
, TranslateType(inColumn.DataType)
, inColumn.MaxLength
, inColumn.AllowDBNull ? ADODB.FieldAttributeEnum.adFldIsNullable :
ADODB.FieldAttributeEnum.adFldUnspecified
, null);
}
result.Open(System.Reflection.Missing.Value
, System.Reflection.Missing.Value
, ADODB.CursorTypeEnum.adOpenStatic
, ADODB.LockTypeEnum.adLockOptimistic, 0);
foreach (DataRow dr in inTable.Rows)
{
result.AddNew(System.Reflection.Missing.Value,
System.Reflection.Missing.Value);
for (int columnIndex = 0; columnIndex < inColumns.Count; columnIndex++)
{
resultFields[columnIndex].Value = dr[columnIndex];
}
}
return result;
}
static ADODB.DataTypeEnum TranslateType(Type columnType)
{
switch (columnType.UnderlyingSystemType.ToString())
{
case "System.Boolean":
return ADODB.DataTypeEnum.adBoolean;
case "System.Byte":
return ADODB.DataTypeEnum.adUnsignedTinyInt;
case "System.Char":
return ADODB.DataTypeEnum.adChar;
case "System.DateTime":
return ADODB.DataTypeEnum.adDate;
case "System.Decimal":
return ADODB.DataTypeEnum.adCurrency;
case "System.Double":
return ADODB.DataTypeEnum.adDouble;
case "System.Int16":
return ADODB.DataTypeEnum.adSmallInt;
case "System.Int32":
return ADODB.DataTypeEnum.adInteger;
case "System.Int64":
return ADODB.DataTypeEnum.adBigInt;
case "System.SByte":
return ADODB.DataTypeEnum.adTinyInt;
case "System.Single":
return ADODB.DataTypeEnum.adSingle;
case "System.UInt16":
return ADODB.DataTypeEnum.adUnsignedSmallInt;
case "System.UInt32":
return ADODB.DataTypeEnum.adUnsignedInt;
case "System.UInt64":
return ADODB.DataTypeEnum.adUnsignedBigInt;
case "System.String":
default:
return ADODB.DataTypeEnum.adVarChar;
}
}
在以上两种方法中,第一种方法,写入数据慢,原因就是程序与Excel交互太过频繁。所以减少两个进程之间的交互,才能缩短导出时间。
标签:
原文地址:http://blog.csdn.net/zc474235918/article/details/45932437