需要 PHP 逐行处理的 CSV 需要很长时间(或超时)才能进入 SQL 数据库
Posted
技术标签:
【中文标题】需要 PHP 逐行处理的 CSV 需要很长时间(或超时)才能进入 SQL 数据库【英文标题】:CSV that needs to be processed row by row by PHP takes ages (or timeouts) to get into a SQL database 【发布时间】:2018-01-10 02:02:46 【问题描述】:我有一个比较网站,我每天晚上都会在该网站上更新来自商店的提要。这些通常不是最大的提要(即最多 15.000 行),但即使是只有 1000 行的 CSV 提要也可能需要很长时间,因为我必须执行(很多?)SQL 查询。这是我的程序:
每个 CSV 提要都来自不同的联属网络,因此具有不同的索引和结构。这就是为什么我编写了一个页面,首先将每个提要标准化为正确的结构(即,一个提要中的 $row['name'] = $col[1],而另一个提要中的 $col[2])。这些“正确”的数据将由另一个函数 CheckProduct(); 处理。 在 CheckProduct() 中,我首先检查产品是否已经存在。如果是,那么我正在更新标准信息:当前价格、库存、网址等。如果不是,我正在将产品插入数据库。但是最后一点需要很长时间,即使是一个额外的查询也可能需要几分钟。我不认为它需要这么长时间,但我一直在寻找这段代码并寻找 SQL 查询优化,但我似乎无法弄清楚如何保持这个过程,当我是批量或组合 CSV行。我已经尝试过优化查询(使用 COUNT() 而不是 num_rows,只选择我需要的列等)。
我不是编程专家,但我知道肯定有不同的选择。我知道你可以将东西存储在一个数组中然后循环它,或者使用 LOAD DATA INFILE,但我不知道在这种情况下如何编程(并且仍然对每一行进行同样的严格检查)。换句话说:如何优化这段代码?
这是函数 CheckProduct()
function CheckProduct($SiteID, $StoreID, $FeedID, $Name, $URL, $Description, $EANSKU, $Image, $Brand, $Color, $Price, $CategoryPath, $Stock, $Deliverycosts, $Deliverytime, $Length, $Width, $Depth, $Height, $Material)
global $db;
if ( !filter_var($URL, FILTER_VALIDATE_URL) === false && !filter_var($Image, FILTER_VALIDATE_URL) === false && !empty($Name) && !empty($URL) && !empty($Price) && !empty($Image) )
$sCountProduct = $db->query("SELECT COUNT(*) as total FROM furniture WHERE name_slug='".CreateSlug(trim($db->real_escape_string($Naam)))."' AND feed_id='".$FeedID."' LIMIT 1");
$fCountProduct = $sCountProduct->fetch_assoc();
if($fCountProduct['total'] == '0')
$iProduct = $db->query("INSERT INTO furniture (site_id,feed_id,store_id,name,name_slug,affiliate_url,description,ean_sku,image_big,brand,brand_slug,color,color_slug,price,category_path,in_stock,visible,shipping_costs,check_today,last_update,deliverytime,length,width,depth,height,material,material_slug,added) VALUES ('1','".$FeedID."','".$StoreID."','".trim($db->real_escape_string($Name))."','".CreateSlug(trim($db->real_escape_string($Name)))."','".trim($AffiliateURL)."','".$db->real_escape_string(trim($Description))."', '".$db->real_escape_string(trim($EANSKU))."','".trim($Afbeelding)."','".$db->real_escape_string(trim($Brand))."','".CreateSlug($db->real_escape_string(trim($Brand)))."','".$db->real_escape_string(trim($Color))."','".CreateSlug($db->real_escape_string(trim($Color)))."','".$db->real_escape_string(trim($Price))."','".$db->real_escape_string(trim(strtolower($CategoryPath)))."','".$Stock."', '3','".$db->real_escape_string(trim($Deliverycosts))."','1','".time()."','".$db->real_escape_string(trim($Deliverytime))."','".$db->real_escape_string(trim($Length))."','".$db->real_escape_string(trim($Width))."','".$db->real_escape_string(trim($Depth))."','".$db->real_escape_string(trim($Height))."','".$db->real_escape_string(trim($Material))."','".CreateSlug($db->real_escape_string(trim($Material)))."','".date('d-m-Y')."')");
$IDProduct = $db->insert_id;
if($iProduct)
$dOthers = $vm->query("DELETE FROM furniture WHERE name_slug='".CreateSlug(trim($db->real_escape_string($Name)))."' AND id != '".$IDProduct."' AND feed_id='".$FeedID."' AND visible != '1'");
else
$sExistProduct = $db->query("SELECT id,site_id,name_slug,feed_id,price_old,visible,price FROM furniture WHERE site_id='1' AND name_slug='".CreateSlug(trim($db->real_escape_string($Name)))."' AND feed_id='".$FeedID."' LIMIT 1");
if(!$sExistProduct)
else
// Check if it is a salesproduct
$fExistProduct = $sExistProduct->fetch_assoc();
$OudePrijs = $fExistProduct['price_old'];
$Zichtbaar = $fExistProduct['visible'];
if($fExistProduct['visible'] == '2') $Visible = '1';
if($Price < $fExistProduct['price']) $OldPrice = $fExistProduct['price'];
$uProduct = $db->query("UPDATE furniture SET affiliate_url='".trim($URL)."', description='".$db->real_escape_string(trim($Description))."', price='".$db->real_escape_string(trim($Price))."', price_old='".$db->real_escape_string(trim($OldPrice))."', in_stock='".$Stock."', shipping_costs='".$db->real_escape_string(trim($Deliverycosts))."', check_today='1', last_update='".time()."', deliverytime='".$Deliverytime."', visible='".$Visible."' WHERE id='".$fExistProduct['id']."' LIMIT 1");
if($uProduct)
// Updated
else
// Error in image (not every image send by the feed is a good one)
这是我处理其中一个提要以匹配正确列的方式:
$SiteID = '1';
$FeedID = '1';
$StoreID = '1';
$Link = 'URL';
if (($handle = fopen($Link, "r")) !== FALSE)
fgetcsv($handle);
$i = 0;
while (($data = fgetcsv($handle, 6000, ";")) !== FALSE)
$num = count($data);
for ($c=0; $c < $num; $c++) $col[$c] = $data[$c];
### VARIABLES FROM FEED ###
$Name = $col[1];
$URL = $col[5];
$Description = strip_tags($col[4]);
$EANSKU = $col[0];
$Image = $col[6];
$Brand = $col[8];
$Color = '';
$Price = $col[3];
$CategoryPath = $col[9];
$Stock = $col[10];
$Deliverycosts = $col[14];
$Deliverytime = '';
$Length = '';
$Width = '';
$Height = '';
$Depth = '';
$Material = '';
### STOCK ###
if($Stock > 0)
$Stock = '1';
else
$Stock = '0';
### SEND PRODUCT ###
CheckProduct($SiteID, $StoreID, $FeedID, $Name, $URL, $Description, $EANSKU, $Image, $Brand, $Color, $Price, $CategoryPath, $Stock, $Deliverycosts, $Deliverytime, $Length, $Width, $Depth, $Height, $Material);
$i++;
fclose($handle);
我正在使用 php 5.6.32 和 mysql 5.0.11。
【问题讨论】:
【参考方案1】:SELECT COUNT(*) 不需要限制...
只需预先选择数据: SELECT name_slug,feed_id, COUNT(*) as cnt as total FROM Furniture GROUP BY name_slug,feed_id");
SELECT name_slug,feed_id, id,site_id,name_slug,feed_id,price_old,visible,price FROM furniture WHERE site_id='1'
并将它们用作查找表。
然后批量插入/删除/更新,整个脚本应该 不到几秒。
或者全部在mysql中完成...
【讨论】:
谢谢。我想我会选择你的最新选择。我现在要做的是:1)使用标准化数据创建一个新的 CSV 文件 2)尝试使用 ON DUPLICATE KEY 加载数据文件。 (来自本主题:***.com/questions/15271202/…)。我认为这将完成这项工作。 :) 如果您的 CSV 行需要唯一,并且所有字段都相同,只需:“cat file.csv | sort | uniq”和“LOAD DATA INFILE”,没有“ON DUPLICATE KEY”会更快以上是关于需要 PHP 逐行处理的 CSV 需要很长时间(或超时)才能进入 SQL 数据库的主要内容,如果未能解决你的问题,请参考以下文章