将大型数据查询(60k +行)导出到Excel

我创建了一个报告工具作为内部Web应用程序的一部分。 该报告在GridView中显示所有结果,并且我使用JavaScript将GridView的内容逐行读取到Excel对象中。 JavaScript继续在不同的工作表上创建数据透视表。

不幸的是,我不希望GridView的大小会导致浏览器超载问题,如果超过几天返回。 该应用程序每天有几千条记录,比如每个月有60k条记录,理想情况下我希望能够将所有结果返回到一年。 行数导致浏览器挂起或崩溃。

我们在带有SQL Server的Visual Studio 2010上使用ASP.NET 3.5,预期的浏览器是IE8。 该报告包含一个gridview,它根据用户选择的群体从少数存储过程中获取数据。 GridView在UpdatePanel中:

<asp:UpdatePanel ID="update_ResultSet" runat="server">
<Triggers>
    <asp:AsyncPostBackTrigger ControlID="btn_Submit" />
</Triggers>
<ContentTemplate>
<asp:Panel ID="pnl_ResultSet" runat="server" Visible="False">
    <div runat="server" id="div_ResultSummary">
        <p>This Summary Section is Automatically Completed from Code-Behind</p>
    </div>
        <asp:GridView ID="gv_Results" runat="server" 
            HeaderStyle-BackColor="LightSkyBlue" 
            AlternatingRowStyle-BackColor="LightCyan"  
            Width="100%">
        </asp:GridView>
    </div>
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>

我对我的团队相对来说比较陌生,所以我遵循了他们将sproc返回给DataTable并将其作为后台代码中的DataSource的典型做法:

    List<USP_Report_AreaResult> areaResults = new List<USP_Report_AreaResult>();
    areaResults = db.USP_Report_Area(ddl_Line.Text, ddl_Unit.Text, ddl_Status.Text, ddl_Type.Text, ddl_Subject.Text, minDate, maxDate).ToList();
    dtResults = Common.LINQToDataTable(areaResults);

    if (dtResults.Rows.Count > 0)
    {
        PopulateSummary(ref dtResults);
        gv_Results.DataSource = dtResults;
        gv_Results.DataBind();

(我知道你在想什么!但是,从那时起,我已经学到了更多关于参数化的知识。)

LINQToDataTable函数没有什么特别之处,只是将列表转换为数据表。

有几千条记录(最多几天),这工作正常。 GridView显示结果,并且有一个按钮让用户点击启动JScript导出器。 外部JavaScript函数将每行读入Excel工作表,然后使用它创建数据透视表。 数据透视表很重要!

function exportToExcel(sMyGridViewName, sTitleOfReport, sHiddenCols) {
//sMyGridViewName = the name of the grid view, supplied as a text
//sTitleOfReport = Will be used as the page header if the spreadsheet is printed
//sHiddenCols = The columns you want hidden when sent to Excel, separated by semicolon (i.e. 1;3;5).
//              Supply an empty string if all columns are visible.

var oMyGridView = document.getElementById(sMyGridViewName);

//If no data is on the GridView, display alert.
if (oMyGridView == null)
    alert('No data for report');
else {
    var oHid = sHiddenCols.split(";");  //Contains an array of columns to hide, based on the sHiddenCols function parameter
    var oExcel = new ActiveXObject("Excel.Application");
    var oBook = oExcel.Workbooks.Add;
    var oSheet = oBook.Worksheets(1);
    var iRow = 0;
    for (var y = 0; y < oMyGridView.rows.length; y++)
    //Export all non-hidden rows of the HTML table to excel.
    {
        if (oMyGridView.rows[y].style.display == '') {
            var iCol = 0;
            for (var x = 0; x < oMyGridView.rows(y).cells.length; x++) {
                var bHid = false;
                for (iHidCol = 0; iHidCol < oHid.length; iHidCol++) {
                    if (oHid[iHidCol].length !=0 && oHid[iHidCol] == x) {
                        bHid = true;
                        break; 
                    } 
                }
                if (!bHid) {
                    oSheet.Cells(iRow + 1, iCol + 1) = oMyGridView.rows(y).cells(x).innerText;
                    iCol++;
                }
            }
            iRow++;
        }
    }

我在做什么:创建一个解决方案(可能是客户端),可以处理这些数据并将其处理到Excel中。 有人可能会建议使用HtmlTextWriter,但afaik不允许自动生成数据透视表并创建令人讨厌的弹出式警告....

我试过的:

  • 填充JSON对象 - 我仍然认为这有潜力,但我还没有找到使其工作的方式。
  • 使用SQLDataSource - 我似乎无法使用它来获取任何数据。
  • 分页和循环浏览页面 - 混合进度。 一般来说很难看,而且我仍然有一个问题,整个数据集被查询并返回显示的每个页面。
  • 更新:我仍然对替代解决方案非常开放,但我一直在追求JSON理论。 我有一个工作的服务器端方法,从DataTable生成JSON对象。 我无法弄清楚如何将该JSON传递给(外部)exportToExcel JavaScript函数....

        protected static string ConstructReportJSON(ref DataTable dtResults)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("var sJSON = [");
            for (int r = 0; r < dtResults.Rows.Count; r++)
            {
                sb.Append("{");
                for (int c = 0; c < dtResults.Columns.Count; c++)
                {
                    sb.AppendFormat(""{0}":"{1}",", dtResults.Columns[c].ColumnName, dtResults.Rows[r][c].ToString());
                }
                sb.Remove(sb.Length - 1, 1); //Truncate the trailing comma
                sb.Append("},");
            }
            sb.Remove(sb.Length - 1, 1);
            sb.Append("];");
            return sb.ToString();
        }
    

    任何人都可以展示一个如何将这个JSON对象带入外部JS函数的例子吗? 或者任何其他解决方案导出到Excel。


    我会尝试使用displaytag来显示结果。 您可以将其设置为每页显示一定数量,这可以解决您的超载问题。 然后,您可以将displaytag设置为允许进行Excel导出。


    我们通常通过一个“导出”命令按钮来处理这个问题,该按钮连接到服务器端的方法来获取数据集并将其转换为CSV。 然后我们调整响应标题,浏览器将其视为下载。 我知道这是一个服务器端解决方案,但您可能需要考虑它,因为在实现服务器端记录分页之前,您将继续发生超时和浏览器问题。


    自从我开始这个问题以来,差不多一个半星期,我终于设法在一定程度上解决了这个问题。 我会暂时等待回答,看看其他人是否有更高效,更好的“最佳实践”方法。

    通过生成一个JSON字符串,我已经离开了GridView的JavaScript。 JSON在数据填充后的代码中生成:

        protected static string ConstructReportJSON(ref DataTable dtResults)
        {
            StringBuilder sb = new StringBuilder();
            for (int r = 0; r < dtResults.Rows.Count; r++)
            {
                sb.Append("{");
                for (int c = 0; c < dtResults.Columns.Count; c++)
                {
                    sb.AppendFormat(""{0}":"{1}",", dtResults.Columns[c].ColumnName, dtResults.Rows[r][c].ToString());
                }
                sb.Remove(sb.Length - 1, 1); //Truncate the trailing comma
                sb.Append("},");
            }
            sb.Remove(sb.Length - 1, 1);
            return String.Format("[{0}]", sb.ToString());
        }
    

    返回一串数据,如

    [{“Caller”:“John Doe”,“Office”:“5555”,“Type”:“传入”等),

    {“Caller”:“Jane Doe”,“Office”:“7777”,“Type”:“传出”等等},{etc}]

    我通过使用以下方法将文本分配给UpdatePanel中的文字来隐藏此字符串:

        <div id="div_JSON" style="display: none;">
                <asp:Literal id="lit_JSON" runat="server" /> 
        </div>
    

    JavaScript通过阅读div的内容来解析输出:

    function exportToExcel_Pivot(sMyJSON, sTitleOfReport, sReportPop) {
         //sMyJSON = the name, supplied as a text, of the hidden element that houses the JSON array.
         //sTitleOfReport = Will be used as the page header if the spreadsheet is printed.
         //sReportPop = Determines which business logic to create a pivot table for.
    
    var sJSON = document.getElementById(sMyJSON).innerHTML;
    var oJSON = eval("(" + sJSON + ")");
    
     //    DEBUG Example Test Code
     //    for (x = 0; x < oJSON.length; x++) {
     //        for (y in oJSON[x])
     //            alert(oJSON[x][y]); //DEBUG, returns field value
     //            alert(y); //DEBUG, returns column name
     //    }
    
    
    //If no data is in the JSON object array, display alert.
    if (oJSON == null)
        alert('No data for report');
    else {
        var oExcel = new ActiveXObject("Excel.Application");
        var oBook = oExcel.Workbooks.Add;
        var oSheet = oBook.Worksheets(1);
        var oSheet2 = oBook.Worksheets(2);
        var iRow = 0;
        var iCol = 0;
    
            //Take the column names of the JSON object and prepare them in Excel
            for (header in oJSON[0])
            {
                oSheet.Cells(iRow + 1, iCol + 1) = header;
                iCol++;
            }
    
            iRow++;
    
            //Export all rows of the JSON object to excel
            for (var r = 0; r < oJSON.length; r++)
            {
                iCol = 0;
                for (c in oJSON[r]) 
                        {
                            oSheet.Cells(iRow + 1, iCol + 1) = oJSON[r][c];
                            iCol++;
                        } //End column loop
                iRow++;
            } //End row
    

    字符串输出和JavaScript'eval'解析的工作速度惊人地快,但是循环遍历JSON对象比我想要的慢一点。

    我相信这种方法将被限制在大约10亿个字符的数据 - 可能更少依赖于内存测试的结果。 (我计算过,我可能每天最多会看100万个字符,所以在报告的一年内应该没问题。)

    链接地址: http://www.djcxy.com/p/21333.html

    上一篇: Export a large data query (60k+ rows) to Excel

    下一篇: Javascript Versions?