将表格数据导出为CSV需要永久

时间:2018-02-15 12:36:13

标签: php symfony csv query-optimization symfony1

我正在从表格数据中生成CSV文件。表记录以百万计。问题是,当我单击EXPORT TO CSV按钮时,生成CSV文件需要6分钟以上,而我的服务器在6分钟后会抛出超时错误。

更新:我知道我可以增加超时时间,但我需要优化以下脚本和QUERY!

将用户导出为CSV的功能

 // Function for exporting all Quiz Users Leads  to CSV
    public function executeUserExportLeads($request) {
        $this->export = true;
        $this->pager = new myPropelPager('BtqUser', 9999999);
        $this->pager->setCriteria(btqAdminBtqUserPeer::getBtqUsersForExport());
        $this->pager->setPeerMethod('doSelectStmt');
        $this->pager->setAction('newadmin/users');
        $this->pager->setPage($request->getParameter('page', 1));
        $this->pager->init();

        //Generating CSV in server and giving user the CSV file for download
        if($this->pager->getResults() > 0){
            $filename = "userleads".".csv";
            unlink($filename);
            $uploaddir  = sfConfig::get('sf_upload_dir');
            $path = $uploaddir ."/" . $filename;
            fopen($path , 'a');
            $handle = fopen($path, 'w+');

            //set column headers
            $fields = array('Date', 'Name', 'Email', 'Lead From', 'State', 'Phone No',"\r\n");
            fwrite($handle, implode(',',$fields));

            //output each row of the data, format line as csv and write to file pointer
            foreach($this->pager->getResults() as $row){
                $lineData = array(date('M-d-Y', strtotime($row['schedule_date'])), $row['name'], $row['email'] , $row['lead_from'], $row['state'], $row['telefon'],"\r\n");
                fwrite($handle, implode(',',$lineData));
            }

            fclose($handle);

            $result_array = array('fileURL' => 'http://this-site.com/uploads/'.$filename);
            return $this->renderText(json_encode($result_array));
        }
        exit;
    }

查询导出(这会获取导出到csv的所有用户记录):

public static function getBtqUsersForExport() {
        $criteria = new Criteria();

        $criteria->clearSelectColumns();
        $criteria->addSelectColumn("btq_user.id as id");
        $criteria->addSelectColumn("btq_user.name as name");
        $criteria->addSelectColumn("btq_user.email as email");
        $criteria->addSelectColumn("btq_user.lead_from as lead_from");
        $criteria->addSelectColumn("btq_user.telefon as telefon");
        $criteria->addSelectColumn("btq_user.datain as datain");


        $criteria->addSelectColumn("state.state as state");

        $criteria->addSelectColumn("lead_schedule.id as schedule_id");
        $criteria->addSelectColumn("lead_schedule.created_at as schedule_date");

        $criteria->addJoin(self::STATE_ID, StatePeer::ID, Criteria::LEFT_JOIN);
        $criteria->addJoin(self::ID, LeadSchedulePeer::LEAD_ID, Criteria::LEFT_JOIN);
        $criteria->addJoin(self::ID, BtqUserTrackBlogVideoPeer::USER_ID, Criteria::LEFT_JOIN);

        $criteria->addGroupByColumn(self::EMAIL);
        $criteria->add(BtqUserPeer::IS_DUMMY_DETAIL, "1", Criteria::NOT_EQUAL);

        $criteria->addDescendingOrderByColumn(self::DATAIN);
        return $criteria;
    }

请求的Ajax:

<script>
    function move() {
        var hidden = document.getElementById("myProgress");
        hidden.classList.remove("hidden");
        var elem = document.getElementById("myBar");
        var width = 1;
        var id = setInterval(frame, 5000);
        function frame() {
            if (width >= 100) {
                clearInterval(id);
                var hidden = document.getElementById("myProgress");
                hidden.classList.add("hidden");
            } else {
                if(width>100){
                }else{
                    width++;
                    elem.style.width = width + '%';
                }
            }
        }
        $('#exportCSV').submit(function(event){
           event.preventDefault();
        });
        $.ajax({
            data: {export: "Export To CSV"},
            type: 'POST',
            url: 'userExportLeads',
            success: function(result){
                console.log(result);
                var data = JSON.parse(result);
                clearInterval(id);
                $('#myBar').css('width','100%');
                $('#myProgress').delay(5000).fadeOut();
                location.href = data.fileURL;
            }
        });
    }
</script>

以下是表单代码:

 <form id="exportCSV" action="<?php echo url_for('newadmin/userExportLeads'); ?>" method="POST">
            <input type="submit" onclick="move()" name="export" value="Export To CSV" />
      </form>

    </div>

    <br/>
      <div id="myProgress" class="hidden" align="left">
          <div id="myBar"></div>
      </div>"

如果还有其他需要,我可以分享。

由于

3 个答案:

答案 0 :(得分:1)

我不确定这是否是主要问题并影响出口执行时间,但重构它会更好

    if ($this->pager->getResults() > 0) {
        foreach ($this->pager->getResults() as $row) {
        }
    }

到这个

    $result = $this->pager->getResults();
    if ($result.count() > 0) { //I'm not sure how myPropelPager works and what is the type of $result. Let's hope it is Countable 
        foreach ($result as $row) {
        }
    }

我也不知道$this->pager->getResults()做了什么。在最坏的情况下,调用它两次,您将执行两次DB查询。 我还希望$result是一个Cursor,而不是包含所有结果的数组。

我的优化可能有助于减少时间和内存使用量,但我不确定。 无论如何,在所有优化之后,如果您要导出数百万行,您将再次遇到此问题。最佳做法是将此过程从Web服务器上下文中分离出来,并在后台使用Gearman在后台进行操作。

答案 1 :(得分:1)

我同意评论中的建议,即切换到原始SQL可能是最好的选择。

即使你不这样做,也要弄清楚SQL Propel正在运行什么,并且你可以做一些优化。在学说中你用echo $query->getSqlQuery()做到了;我不熟悉如何在Propel中做到这一点。在这个慢速查询运行时,您也可以连接到MySQL,SHOW FULL PROCESSLIST应该显示正在运行的查询。

使用EXPLAIN对查询进行前缀,您可以看到MySQL的查询计划; EXPLAIN SELECT如果您之前没有使用它,则不是很直观,但在网上有很好的指南。

答案 2 :(得分:-1)

增加您的DBMS连接读取时间。