需要通过 PHP 将大型 CSV 文件导入多个 MySQL 表的高效方法
Posted
技术标签:
【中文标题】需要通过 PHP 将大型 CSV 文件导入多个 MySQL 表的高效方法【英文标题】:Need Time-Efficient Method of Importing Large CSV File Via PHP Into Multiple MySQL Tables 【发布时间】:2011-04-23 22:27:21 【问题描述】:好的,我在这里遇到了一些严重的问题。我是这个网站的新手,不熟悉通过 php 导入 CSV 数据,但我对编程并不陌生。
目前,我正在构建客户关系经理。我需要创建一个脚本来导入一个文件,该文件将用潜在客户填充数据库。这里的主要问题是潜在客户数据由该公司的公司和员工组成。此外,还有一些其他表从主表中分离出来,例如帐单信息。
我有一个工作脚本,允许用户将导入的数据映射到特定的行和列。
function mapData($file)
// Open the Text File
$fd = fopen($file, "r");
// Return FALSE if file not found
if(!$fd)
return FALSE;
// Get the First Two Lines
$first = 0;
$data = array();
while(!feof($fd))
if($first == 0)
$cols = fgetcsv($fd, 4096);
$data['cols'] = array();
if(is_array($cols) && count($cols))
foreach($cols as $col)
if(!$col)
continue;
$data['cols'][] = $col;
if(empty($data['cols']))
return array();
$first++;
continue;
else
$data['first'] = fgetcsv($fd, 4096);
break;
fclose($fd);
// Return Data
return $data;
上述脚本仅在 CodeIgniter 将文件移动到工作目录后激活。我已经知道文件名是什么了。该文件进入并返回列列表和第一行。任何空列都会被忽略。
在此之后,进程传递到映射脚本。一旦映射完成并按下“导入”,这段代码就会加载。
function importLeads($file, $map)
// Open the Text File
if(!file_exists($file))
return false;
error_reporting(E_ALL);
set_time_limit(240);
ini_set("memory_limit", "512M");
$fd = fopen($file, "r");
// Return FALSE if file not found
if(!$fd)
return FALSE;
// Traverse Each Line of the File
$true = false;
$first = 0;
while(!feof($fd))
if($first == 0)
$cols = fgetcsv($fd);
$first++;
continue;
// Get the columns of each line
$row = fgetcsv($fd);
// Traverse columns
$group = array();
$lead_status = array();
$lead_type = array();
$lead_source = array();
$user = array();
$user_cstm = array();
$user_prof = array();
$acct = array();
$acct_cstm = array();
$acct_prof = array();
$acct_group = array();
if(!$row)
continue;
foreach($row as $num => $val)
if(empty($map[$num]))
continue;
$val = str_replace('"', """, $val);
$val = str_replace("'", "'", $val);
switch($map[$num])
// Company Account
case "company_name":
$acct['company_name'] = $val;
break;
case "lead_type":
$lead_type['name'] = $val;
break;
case "lead_source":
$lead_source['name'] = $val;
break;
case "lead_source_description":
$lead_source['name'] = $val;
break;
case "campaign":
$campaign['name'] = $val;
break;
case "mcn":
$acct['mcn'] = $val;
break;
case "usdot":
$acct['usdot'] = $val;
break;
case "sic_codes":
$acct_cstm['sic_codes'] = $val;
break;
case "naics_codes":
$acct_cstm['naics_codes'] = $val;
break;
case "agent_assigned":
$acct_cstm['agent_assigned'] = $val;
break;
case "group_assigned":
$group['name'] = $val;
break;
case "rating":
$acct_cstm['rating'] = $val;
break;
case "main_phone":
$acct['phone'] = $val;
break;
case "billing_phone":
$acct_cstm['billing_phone'] = $val;
break;
case "company_fax":
$acct['fax'] = $val;
break;
case "company_email":
$acct['email2'] = $val;
break;
// Company Location
case "primary_address":
$acct['address'] = $val;
break;
case "primary_address2":
$acct['address2'] = $val;
break;
case "primary_city":
$acct['city'] = $val;
break;
case "primary_state":
$acct['state'] = $val;
break;
case "primary_zip":
$acct['zip'] = $val;
break;
case "primary_country":
$acct['country'] = $val;
break;
case "billing_address":
$billing['address'] = $val;
break;
case "billing_address2":
$billing['address2'] = $val;
break;
case "billing_city":
$billing['city'] = $val;
break;
case "billing_state":
$billing['state'] = $val;
break;
case "billing_zip":
$billing['zip'] = $val;
break;
case "billing_country":
$billing['country'] = $val;
break;
case "company_website":
$acct_cstm['website'] = $val;
break;
case "company_revenue":
$acct_cstm['revenue'] = $val;
break;
case "company_about":
$acct_prof['aboutus'] = $val;
break;
// Misc. Company Data
case "bols_per_mo":
$acct_cstm['approx_bols_per_mo'] = $val;
break;
case "no_employees":
$acct_cstm['no_employees'] = $val;
break;
case "no_drivers":
$acct_prof['drivers'] = $val;
break;
case "no_trucks":
$acct_prof['power_units'] = $val;
break;
case "no_trailers":
$acct_cstm['no_trailers'] = $acct_prof['trailers'] = $val;
break;
case "no_parcels_day":
$acct_cstm['no_parcels_day'] = $val;
break;
case "no_shipping_locations":
$acct_cstm['no_shipping_locations'] = $val;
break;
case "approves_inbound":
$acct_cstm['approves_inbound'] = $val;
break;
case "what_erp_used":
$acct_cstm['what_erp_used'] = $val;
break;
case "birddog":
$acct_cstm['birddog_referral'] = $val;
break;
case "status_notes":
$acct_cstm['status_notes'] = $val;
break;
case "notes":
$acct_cstm['notes'] = $val;
break;
case "internal_notes":
$acct_cstm['notes_internal'] = $val;
break;
// User Data
case "salutation":
$user_cstm['salutation'] = $val;
break;
case "first_name":
$user['first_name'] = $billing['first_name'] = $val;
break;
case "last_name":
$user['last_name'] = $billing['last_name'] = $val;
break;
case "user_title":
$user_prof['title'] = $val;
break;
case "user_about":
$user_prof['about'] = $val;
break;
case "user_email":
$user['email'] = $val;
break;
case "home_phone":
$user_prof['phone'] = $val;
break;
case "mobile_phone":
$user_cstm['mobile_phone'] = $val;
break;
case "direct_phone":
$user_cstm['direct_phone'] = $val;
break;
case "user_fax":
$user_prof['fax'] = $val;
break;
case "user_locale":
$user['location'] = $val;
break;
case "user_website":
$user_prof['website_url'] = $val;
break;
case "user_facebook":
$user_prof['fb_url'] = $val;
break;
case "user_twitter":
$user_prof['twitter_url'] = $val;
break;
case "user_linkedin":
$user_prof['linkedin_url'] = $val;
break;
if(empty($acct['company_name']) || empty($user['first_name']) || empty($user['last_name']))
continue;
$this->db = $this->load->database('crm_db', TRUE);
if(isset($lead_type['name']) && ($name = $lead_type['name']))
$count = $this->db->count_all("lead_types");
$check = $this->db->get_where("lead_types", array("name" => $name));
if($check->num_rows() < 1)
$this->db->insert("lead_types", array("name" => $name, "order" => $count));
$ltype = $this->db->insert_id();
$acct_cstm['lead_type'] = $acct['account_type'] = $user['company_type'] = $ltype;
if(isset($lead_source['name']) && ($name = $lead_source['name']))
$count = $this->db->count_all("lead_sources");
$check = $this->db->get_where("lead_sources", array("name" => $name));
if($check->num_rows() < 1)
$this->db->insert("lead_sources", array("name" => $name, "order" => $count));
$acct_cstm['lead_source'] = $this->db->insert_id();
if(isset($campaign['name']) && ($name = $campaign['name']))
$check = $this->db->get_where("campaigns", array("name" => $name));
if($check->num_rows() < 1)
$campaign['id'] = $accounts_cstm['campaign'] = $this->Secure_m->generate_sugar_id();
$campaign['date_entered'] = time();
$campaign['date_modified'] = time();
$campaign['modified_user_id'] = $this->session->userdata('id');
$campaign['created_by'] = $this->session->userdata('id');
$this->db->insert("campaigns", $campaign);
if(isset($group['name']) && ($name = $group['name']))
$order = $this->db->count_all("groups");
$check = $this->db->get_where("groups", array("name" => $name));
if($check->num_rows() < 1)
$this->db->insert("groups", array("name" => $name, "order" => $order));
$acct_group['id'] = $this->db->insert_id();
$mem = new stdclass;
$uid = 0;
if(is_array($user) && count($user))
$where = "";
if(!empty($user['phone']))
$where .= "prof.phone = '$user['phone']' OR ";
$where .= "cstm.mobile_phone = '$user['phone']' OR ";
$where .= "cstm.direct_phone = '$user['phone']'";
if(!empty($user['mobile_phone']))
if($where)
$where .= " OR ";
$where .= "prof.phone = '$user['mobile_phone']' OR ";
$where .= "cstm.mobile_phone = '$user['mobile_phone']' OR ";
$where .= "cstm.direct_phone = '$user['mobile_phone']'";
if(!empty($user['direct_phone']))
if($where)
$where .= " OR ";
$where .= "prof.phone = '$user['direct_phone']' OR ";
$where .= "cstm.mobile_phone = '$user['direct_phone']' OR ";
$where .= "cstm.direct_phone = '$user['direct_phone']'";
$query = $this->db->query($this->Account_m->userQuery($where));
$mem = reset($query->result());
if($where && !empty($mem->id))
$uid = $mem->id;
$new = array();
foreach($user as $k => $v)
if(!empty($mem->$k))
$new[$k] = $mem->$k;
unset($user[$k]);
else
$new[$k] = $v;
//$this->db->update("leads", $user, array("id" => $uid));
$user = $new;
else
$user['uxtime'] = time();
$user['isclient'] = 0;
$user['flag'] = 0;
$user['activation_code'] = $this->Secure_m->generate_activate_id();
$uid = $this->Secure_m->generate_activate_id(10);
$query = $this->db->get_where("leads", array("id" => $uid), 1);
$data = reset($query->result());
while(!empty($data->id))
$uid = $this->Secure_m->generate_activate_id(10);
$query = $this->db->get_where("leads", array("id" => $uid), 1);
$data = reset($query->result());
$user['id'] = $uid;
$this->db->insert("leads", $user);
if($uid && is_array($user_prof) && count($user_prof))
if(!empty($mem->uid))
$new = array();
foreach($user_prof as $k => $v)
if(!empty($mem->$k))
$new[$k] = $mem->$k;
unset($user_prof[$k]);
else
$new[$k] = $v;
//$this->db->update("mprofiles", $user_prof, array("uid" => $uid));
$user_prof = $new;
else
$user_prof['uid'] = $uid;
$user_prof['flag'] = 0;
$this->db->insert("ldetails", $user_prof);
if($uid && is_array($user_cstm) && count($user_cstm))
$query = $this->db->get_where("leads_cstm", array("crm_id" => $cid), 1);
$data = reset($query->result());
if(!empty($data->crm_id))
$new = array();
foreach($user_cstm as $k => $v)
if(!empty($mem->$k))
$new[$k] = $mem->$k;
unset($user_cstm[$k]);
else
$new[$k] = $v;
//$this->db->update("leads_cstm", $acct_prof, array("fa_user_id" => $cid));
$user_cstm = $new;
else
$user_cstm['crm_id'] = $uid;
$user_cstm['date_entered'] = time();
$user_cstm['date_modified'] = time();
$user_cstm['created_by'] = $this->session->userdata('id');
$user_cstm['modified_user_id'] = $this->session->userdata('id');
$this->db->insert("leads_cstm", $user_cstm);
$cmp = new stdclass;
$cid = 0;
if(is_array($acct) && count($acct))
$acct['uid'] = $uid;
$acct['main_contact'] = "$user['first_name'] $user['last_name']";
if(!empty($user['email']))
$acct['email'] = $user['email'];
$acct['isprospect'] = 0;
$acct['flag'] = 0;
if(!empty($acct['mcn']))
$where .= "fms.mcn = '$acct['mcn']'";
if(!empty($acct['phone']))
if($where)
$where .= " OR ";
$where .= "fms.phone = '$acct['phone']' OR ";
$where .= "crm.billing_phone = '$acct['phone']'";
if(!empty($acct['billing_phone']))
if($where)
$where .= " OR ";
$where .= "fms.phone = '$acct['billing_phone']' OR ";
$where .= "crm.billing_phone = '$acct['billing_phone']'";
if(!empty($acct['company_name']))
if($where)
$where .= " OR ";
$where .= "fms.company_name = '$acct['company_name']'";
$query = $this->db->query($this->Account_m->acctQuery($where));
$cmp = reset($query->result());
if($where && !empty($cmp->id))
$cid = $cmp->id;
$new = array();
foreach($acct as $k => $v)
if(!empty($cmp->$k))
$new[$k] = $cmp->$k;
unset($acct[$k]);
else
$new[$k] = $v;
//$this->db->update("accounts", $billing, array("cid" => $cid));
$acct = $new;
else
$cid = $this->Secure_m->generate_activate_id(10);
$query = $this->db->get_where("leads", array("id" => $uid), 1);
$data = reset($query->result());
while(!empty($data->id))
$cid = $this->Secure_m->generate_activate_id(10);
$query = $this->db->get_where("accounts", array("id" => $cid), 1);
$data = reset($query->result());
$acct['id'] = $cid;
$this->db->insert("accounts", $acct);
if($cid && is_array($acct_group) && count($acct_group))
$grp = $this->db->get_where("accounts_groups", array("cid" => $cid, "gid" => $acct_group['id']));
if(empty($cmp->id))
$acct_group['cid'] = $cid;
$this->db->insert("accounts_groups", $acct_group);
if($cid && is_array($acct_prof) && count($acct_prof))
if(!empty($cmp->id))
$new = array();
foreach($acct_prof as $k => $v)
if(!empty($cmp->$k))
$new[$k] = $cmp->$k;
unset($acct_prof[$k]);
else
$new[$k] = $v;
//$this->db->update("cprofiles", $acct_prof, array("cid" => $cid));
$acct_prof = $new;
else
$acct_prof['cid'] = $cid;
$acct_prof['flag'] = 0;
$this->db->insert("adetails", $acct_prof);
if($cid && is_array($billing) && count($billing))
$bill = $this->db->get_where("accounts_billing", array("cid" => $cid));
if(!empty($bill->id))
$new = array();
foreach($acct_prof as $k => $v)
if(!empty($cmp->$k))
$new[$k] = $cmp->$k;
unset($acct_prof[$k]);
else
$new[$k] = $v;
//$this->db->update("accounts_billing", $billing, array("cid" => $cid));
else
$billing['cid'] = $cid;
$billing['flag'] = 0;
$this->db->insert("accounts_billing", $billing);
if($cid && $uid)
$this->db->update("leads", array("cid" => $cid), array("id" => $uid));
if($cid && is_array($acct_cstm) && count($acct_cstm))
$query = $this->db->get_where("accounts_cstm", array("crm_id" => $cid), 1);
$data = reset($query->result());
if(!empty($data->crm_id))
$new = array();
foreach($acct_cstm as $k => $v)
if(!empty($cmp->$k))
$new[$k] = $cmp->$k;
unset($acct_cstm[$k]);
else
$new[$k] = $v;
//$this->db->update("accounts_cstm", $acct_cstm, array("crm_id" => $cid));
$acct_cstm = $new;
else
$acct_cstm['crm_id'] = $cid;
$acct_cstm['date_entered'] = time();
$acct_cstm['date_modified'] = time();
$acct_cstm['created_by'] = $this->session->userdata('id');
$acct_cstm['modified_user_id'] = $this->session->userdata('id');
if(empty($acct_cstm['rating']))
$acct_cstm['rating'] = 1;
$this->db->insert("accounts_cstm", $acct_cstm);
$true = TRUE;
fclose($fd);
return $true;
现在,据我所知,脚本运行良好。实际代码本身没有任何问题。问题是在大约 400-500 行之后,脚本就停止了。我没有收到错误,但没有处理更多代码。
我知道这一点是因为我在此之后有代码应该通过 AJAX 返回重定向页面。不过,在 importLeads 函数中的循环之后,什么都没有加载。
我不确定如何让这个脚本更高效...我很肯定它正在超时,但我不知道如何让它更高效地运行。我需要这个脚本来分别处理上面的所有信息。我有各种单独的表,它们都链接在一起,这个导入脚本必须以不同的方式设置所有内容。
我已经与我的客户讨论过这个项目。当我把它放到 400 行左右时,这个脚本就可以工作了。他有很多这样的 CSV 文件,大约 75,000 行。我要导入的是一个较小的,只有大约 1,200 行。
我尝试过寻找替代方法,例如 mysql 的导入脚本,但我不能这样做,因为该脚本必须将数据导入单独的表中,并且必须首先检查现有数据。我还应该使用导入的信息更新所有空字段,但这会使情况变得更糟。
如果有人知道更有效的方法,将不胜感激。我试图尽可能详细。值得注意的是,我会提到我正在使用 CodeIgniter,但如果有更有效的方法不使用 CodeIgniter,我会采用它(不过我仍然可以将它放入 CI 模型中)。
【问题讨论】:
我在尝试自动下载 150 个 .csv 文件(其中一些是 GB 大小的数百万行)并将它们导入 mysql 数据库时遇到了这个问题。即使在确保我的所有 PHP、MySQL 和 Apache 超时都被禁用之后,脚本也会在循环期间简单地停止……而不会显示任何错误消息。 (即使在使用 Firebug 时也没有)结果是浏览器对我超时了。我必须在每次插入后回显一个句点 (.) 以保持浏览器处于活动状态。 (我目前正在研究其他替代方案,例如页面上的隐形计数器以使其保持活动状态 【参考方案1】:我编写了 PHP 脚本来批量加载 Stack Overflow 数据转储发布的数据。我导入了数百万行,并且不需要那么长时间。
这里有一些提示:
不要依赖自动提交。为每一行启动和提交事务的开销是巨大的。使用显式事务,并在每 1000 行(或更多)之后提交。
使用准备好的语句。由于您基本上执行相同的插入数千次,您可以在开始循环之前准备每个插入,然后在循环期间执行,将值作为参数传递.我不知道如何使用 CodeIgniter 的数据库来做到这一点,你必须弄清楚。
调整 MySQL 以进行导入。增加缓存缓冲区等。请参阅Speed of INSERT Statements 了解更多信息。
使用 LOAD DATA INFILE。 如果可能。它比使用 INSERT 逐行加载数据快 20 倍。我了解您是否不能因为您需要获取最后一个插入 ID 等等。但在大多数情况下,即使您读取 CSV 文件,重新排列并将其写入多个临时 CSV 文件,数据加载仍然比使用 INSERT 更快。
离线执行。不要在网络请求期间运行长时间运行的任务。 PHP 请求的时间限制将终止作业,如果不是今天,那么下周二,当作业长 10% 时。相反,让 Web 请求排队作业,然后将控制权返回给用户。您应该将数据导入作为服务器进程运行,并定期允许用户查看进度。例如,一种廉价的方法是让您的导入脚本输出“。”到临时文件,然后用户可以请求查看临时文件并继续在浏览器中重新加载。如果您想变得花哨,请使用 Ajax。
【讨论】:
好的,感谢您的这些提示。我阅读了插入语句的速度,现在我知道你所说的自动提交是什么意思......我想我要做的是在引擎上运行它(这个引擎已经在系统上设置,我只是没有知道如何工作),它将每隔几分钟加载 100 行,而不是对每一行进行自动插入,而是在每次脚本加载时生成一个长查询以将所有这些都加载到一个插入中。如果这在速度方面也能更好地工作,我可能可以将它提高到 400-500 左右而不会造成太多麻烦。我会做更多的工作,看看进展如何。谢谢! 好的,进度更新。我已经完成了脚本的更新并重新编写了所有内容,并修复了所有错误。我重写了脚本,使其没有这么长的 switch 语句(如下所述),而且还简单地抓取数据并创建一系列包含所有要插入的字段数据的数组,并且我还添加了检查以计算插入的ID(检查最大 id 或返回当前行号)。到达文件末尾后,将创建并运行插入语句。 1,200 行需要几秒钟。我会尝试更长的时间,但效果会更好。【参考方案2】:要在 MySQL 中有效地导入数据,您必须使用LOAD DATA INFILE。这将对性能产生巨大影响。
如果您需要对数据进行预处理,请使用上述脚本执行此操作,然后导出回 CSV/TSV 并使用 LOAD DATA 查询最终导入您的数据库。
您的脚本不会超过 500 行,因为它很可能会达到 PHP 执行时间限制。您可以使用set_time_limit() 函数给您的脚本完全没有时间限制,在这种情况下,您必须在脚本开始时调用set_time_limit(0)
。
【讨论】:
哦,不。这是我的一次意外的否决票。在我回到页面并意识到我的错误之后,已经来不及撤消它了。也许您可以编辑您的答案,以便我可以撤消它,或者有其他方法吗? 我对否决票有点困惑,感谢您的评论。我做了一个小修改。 可能可行的方法是创建一个名为 import_leads 的表,其中包含我需要的所有字段,然后调用 LOAD DATA INFILE 并稍后处理该数据副本......但是,我想我已经找到另一种解决方案是稍微清理一下我的脚本并通过一个引擎运行它,该引擎每隔几分钟就会加载 100 个(如果我可以改进脚本的处理,可能会加载大约 400-500 个)。数据不会立即可用,但应该没问题。【参考方案3】:我必须提出的另一项是,此代码是否需要发生以下情况:
目前您在哪里执行此操作:
foreach($row as $num => $val)
if(empty($map[$num]))
continue;
$val = str_replace('"', """, $val);
$val = str_replace("'", "'", $val);
switch($map[$num])
// Company Account
case "company_name":
$acct['company_name'] = $val;
break;
您需要更改开关/外壳来执行此操作:
1) 创建映射字段的数据映射。 数据映射应具有字段映射到的正确数组以及该数组的索引。 例如:
$dataMap['company_name'] = array($acct, 'company_name');
$dataMap['lead_type'] = array($lead_type, 'name');
.
.
.
$dataMap['bols_per_mo'] = array($acct_cstm, 'approx_bols_per_mo');
.
.
.
等等
然后 2)用这个简单的代码sn-p替换你的大量switch语句:
foreach($row as $num => $val)
if(empty($map[$num]))
continue;
$val = str_replace('"', """, $val);
$val = str_replace("'", "'", $val);
$mappingRecord = $dataMap[ $map[$num] ];
//The first element is the array the data should go in
$destinationArray = $mappingRecord[0];
//the second element is the index of the array it should go in
$destinationArray[$mappingRecord[1]] = $val;
【讨论】:
这不是问题的完整解决方案,但这肯定会有很大帮助。我遇到的问题之一是写出那个巨大的开关……这看起来好多了,而且运行的行更少。【参考方案4】:使用加载数据 infile 方法将原始 csv 文件数据加载到暂存(临时)表中,该方法既好又快:
set autocommit = 0;
load data infile..
load data infile..
...
commit;
加载数据后,运行数据清理、映射和验证存储过程等:
call cleanse_staging_data();
call map_staging_data();
call validate_staging_data();
处理完数据后,将暂存表中的数据复制到正确的数据表中:
call copy_staging_to_production();
或类似的东西。
【讨论】:
【参考方案5】:您是否达到了加载大文件的 PHP 脚本时间限制?
试试这个:
set_time_limit(0);
禁用默认的 30 秒时间限制。您可以使用max_execution_time()
检索服务器范围的限制。如果服务器范围的限制对于此批量加载作业来说不够长,您需要弄清楚如何让本地服务器管理员更改它或以其他方式上传。
【讨论】:
【参考方案6】:有时我不得不做类似的事情。您可能会遇到几个问题:
-
PHP 脚本超时 - 在预定义的一段时间后 PHP 会自动终止进程。您可以在 php.ini 文件中或使用
set_time_limit(0)
禁用此功能。但是,有些主机禁用了这种方法,有些主机设置了单独的 proc watch 来杀死已经运行了一段时间的进程,以防止服务器被关闭。
内存限制 - PHP 将允许您在同一个 php.ini 中设置最大内存限制。如果它击中它,它将触发致命错误并死亡。您可以在错误日志中看到这一点,但不会向浏览器输出任何内容。
MySQL 查询开销 – 正如其他人所指出的,每个单独的查询都有很多开销。我没有足够的行数来证明将load data infile
投入风暴是合理的。我也不需要为每个单独的查询获取结果,所以我只需将它们全部放在一个调用中并将其发送到单个 mysql_query (您可以从 CodeIgniter 中的控制器执行此操作,如下所示:mysql_query($sql, $this->db->conn_id);
since如果你将它传递给 DB::query,它会很合适)。
Bill Karwin 在处理非常大的数据集时指出了许多优化的好方法,但如果您在处理大约 400 行时遇到问题,我认为这对您没有多大帮助.检查您的错误日志,解决问题,然后进行优化。
【讨论】:
以上是关于需要通过 PHP 将大型 CSV 文件导入多个 MySQL 表的高效方法的主要内容,如果未能解决你的问题,请参考以下文章
使用 ADO 将大型 csv 文件导入 mdb 时的性能问题