我正在从表格数据中生成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>"
如果还有其他需要,我可以分享。
由于
答案 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连接读取时间。