Перейти к содержимому


Партнерская программа Kredov

Обработка большого текстового файла

#1 gaaarfild

gaaarfild
  • Пользователь
  • 596 сообщений
  • Репутация: 0
0

Отправлено 15 Сентябрь 2010 - 16:11

Есть такая опция, когда пользователи обновляют Базу данных с помощью CVS файла.
Но файлы достаточно большие. 1-2 мегабайта.
Каждая строчка содержит госномер машины, VIN и компанию, которой принадлежит машина, а так же периоды страховки ее.
При загрузке в БД идет сначала проверка, нет ли такого авто уже в БД. Если есть, то его необходимо просто обновить. Если нет, то добавить новую запись.
У меня данная конструкция получается очень сильно громоздкой.
И скрипт есть очень много ресурсов, потому что очень много запросов в БД. Подскажите пожалуйста, как можно максимально оптимизированно обрабатывать файл?

Вот пример CSV файла.

SkID;nomZvl;vidStr;VIN;gosNom;model;dts;dtPo;dtSl
HTN;803р-08АлФ;1;XTA21102020541895;;ВАЗ 2110;2008-7-31;2009-7-30;2008-11-20
HTN;805р-08АлФ;1;X8927851B50BE3893;;БАГЕМ 278510;2008-6-24;2009-6-23;2008-11-20
HTN;806р-08АлФ;1;KLAJF19W1VB201104;Х468РР;DAEWOO ESPERO;2008-8-4;2009-8-3;2008-11-21
HTN;807р-08АлФ;1;XTA21074011442260;Х250РН;ВАЗ 2107;2008-9-2;2009-9-1;2008-11-24
HTN;808р-08АлФ;1;XTA210990T1820120;T006ЕУ;ВАЗ 21099;2008-4-30;2009-4-29;2008-11-24
HTN;809р-08АлФ;1;JMBSNCS3A6U017800;Х606АУ;MITSUBISHI LANCER;2008-5-2;2009-5-1;2008-11-25


Вот код обработки
if($_FILES['usercsv']['tmp_name']!="") {
if($files_class->get_typefile($_FILES['usercsv']['name'])!='csv') {
header('Location: index.php?mod=carbaseedit&do=Add&error=filetypeerror');
}
$che = 1;
do {
$filename = date('d-m-Y_H-i-s').'('.$che.').csv';
//$filename = rand(1000000, 9000000).'.csv';
$che++;
} while (file_exists('uploads/tmp/'.$filename));

if(!copy($_FILES['usercsv']['tmp_name'], 'uploads/tmp/'.$filename)) {
header('Location: index.php?mod=carbaseedit&do=Add&error=uploaderror');
} else {
//setlocale(LC_ALL, 'ru_RU.UTF-8');
$file_size = filesize('uploads/tmp/'.$filename)+100;
$fo = fopen('uploads/tmp/'.$filename, "r");
$row = 1;
//$db->sql_query("TRUNCATE TABLE ".$prefix."_car_rollback");
$records_sum = 0;
$filecont = file_get_contents('uploads/tmp/'.$filename);
while (($line = fgets($fo, $file_size)) !== false) {
if($row!=1) {
$data=explode(';', $line);
// [0] - Код компании
// [1] - Заявление
// [2] - Каско\Осаго
// [3] - VIN
// [4] - Госномер
// [5] - Модель машины
// [6] - Дата С
// [7] - Дата По
// [8] - Дата случая

list($cid) = $db->sql_fetchrow($db->sql_query("SELECT id FROM ".$prefix."_ins_companies WHERE `tid` LIKE '".$data[0]."'"));
$gosnom_sh = str_replace(' ', '', $data[4]);
//$gosnom = iconv('cp1251', 'utf-8//IGNORE', $gosnom);
$gosnom_sh = $texts_class->translit_toeng_repl($gosnom_sh);
$dat_adate = explode('-', $data[8]);
$region = mb_substr($gosnom_sh, 7);
$vin = str_replace(' ', '', $data[3]);
$dat_from = explode('-', $data[6]);
$date_fr = mktime(3,0,0, $dat_from[1], $dat_from[2], $dat_from[0]);
$dat_to = explode('-', $data[7]);
$date_to = mktime(3,0,0, $dat_to[1], $dat_to[2], $dat_to[0]);
$dat_adate = explode('-', $data[8]);
$adate = mktime(3,0,0, $dat_adate[1], $dat_adate[2], $dat_adate[0]);
$result = $db->sql_query("SELECT id FROM ".$prefix."_car WHERE `company`=".$cid." AND `gosnomer`='".$gosnom_sh."' AND `vin`='".$vin."' AND `adate`=".$adate."");
if($db->sql_numrows($result)==0) {
$gosnom_sh = $texts_class->translit_toeng_repl($gosnom_sh);
$result = $db->sql_query("SELECT id FROM ".$prefix."_car WHERE `company`=".$cid." AND `gosnomer`='".$gosnom_sh."' AND `vin`='".$vin."' AND `adate`=".$adate."");
}
//print_r($db->sql_error());
if($db->sql_numrows($result)==0) {
$gosnom_sh = $texts_class->translit_toeng_repl($gosnom_sh);
$result1 = $db->sql_query("INSERT INTO ".$prefix."_car (`gosnomer`, `region`, `model`, `claim`, `company`, `company_code`, `ins_type`, `fr`, `fr_d`, `to`, `to_d`, `adate`, `adate_d`, `vin`, `add_time`, `uinput`) VALUES ('".$gosnom_sh."', '".$regison."', '".mysql_escape_string(str_replace('"', '', trim($data[5])))."', '".mysql_escape_string(trim($data[1]))."', ".$cid.", '".mysql_escape_string(trim($data[0]))."', ".intval($data[2]).", ".$date_fr.", '".mysql_escape_string(trim($data[6]))."', ".$date_to.", '".mysql_escape_string(trim($data[7]))."', ".$adate.", '".mysql_escape_string(trim($data[8]))."', '".$vin."', ".time().", ".intval($_SESSION['user_id']).")");
$newcar_id = $db->sql_nextid($result1);
$db->sql_query("INSERT INTO ".$prefix."_car_rollback (`car_id`, `time`, `company`) VALUES (".$newcar_id.", ".time().", ".intval($_SESSION['company_id']).")");
$db->sql_query("INSERT INTO ".$prefix."_ins_period (`fr`, `to`, `car_id`, `add_time`, `uinput`) VALUES (".$date_fr.", ".$date_to.", ".$newcar_id.", ".time().", ".intval($_SESSION['user_id']).")");
$db->sql_query("INSERT INTO ".$prefix."_accident (`adate`, `car_id`, `add_time`, `uinput`) VALUES (".$adate.", ".$newcar_id.", ".time().", ".intval($_SESSION['user_id']).")");
//print_r($db->sql_error());
} else {
list($sid) = $db->sql_fetchrow($result);
$gosnom_sh = $texts_class->translit_toeng_repl(trim($gosnom_sh));
$db->sql_query("UPDATE ".$prefix."_car SET `gosnomer`='".$gosnom_sh."', `region`='".trim($region)."', `model`='".mysql_escape_string(trim($data[5]))."', `claim`='".mysql_escape_string(trim($data[1]))."', `company`=".$cid.", `company_code`='".mysql_escape_string(trim($data[0]))."', `ins_type`=".intval($data[2]).", `fr`=".$date_fr.", `fr_d`='".mysql_escape_string(trim($data[6]))."', `to`=".$date_to.", `to_d`='".mysql_escape_string(trim($data[7]))."', `adate`=".$adate.", `adate_d`='".mysql_escape_string(trim($data[8]))."', `vin`='".$vin."', `uinput`=".intval($_SESSION['user_id'])." WHERE id=".$sid."");
$result3 = $db->sql_query("SELECT id FROM ".$prefix."_ins_period WHERE `car_id`=".$sid." AND `fr`=".$date_fr." AND `to`=".$date_to."");
if($db->sql_numrows($result3)==0) {
$db->sql_query("INSERT INTO ".$prefix."_ins_period (`fr`, `to`, `car_id`, `add_time`, `uinput`) VALUES (".$date_fr.", ".$date_to.", ".$sid.", ".time().", ".intval($_SESSION['user_id']).")");
}
$result4 = $db->sql_query("SELECT id FROM ".$prefix."_accident WHERE `car_id`=".$sid." AND `adate`=".$adate."");
if($db->sql_numrows($result4)==0) {
$db->sql_query("INSERT INTO ".$prefix."_accident (`adate`, `car_id`, `add_time`, `uinput`) VALUES (".$adate.", ".$newcar_id.", ".time().", ".intval($_SESSION['user_id']).")");
}
//print_r($db->sql_error());
}
}
$records_sum = ($row!=1) ? ++$records_sum : $records_sum;
$row++;

}
//unlink('uploads/tmp/'.$filename);
$db->sql_query("INSERT INTO ".$prefix."_upload_stats (`date`, `company`, `uid`, `filename`, `filesize`, `records`) VALUES (".time().", ".intval($cid).", ".intval($_SESSION['user_id']).", '".$filename."', '".$_FILES['usercsv']['size']."', ".$records_sum.")");
print_r($db->sql_error());
// Логирование начало
$core_class->log_text_db = 'filename: '.mysql_escape_string($_FILES['usercsv']['name']).' -> '.$filename.', filesize: '.intval($file_size).'b';
$core_class->log_text_file = 'filename: '.mysql_escape_string($_FILES['usercsv']['name']).' -> '.$filename.', filesize: '.intval($file_size).'b';
$core_class->log_to_file('carbase_load_csv');
$core_class->log_to_db('carbase_load_csv');
//Логирование конец
header('Location: index.php?mod=carbaseedit&do=Add&error=noerror');
}
} else {
header('Location: index.php?mod=carbaseedit&do=Add&error=nofile');
}

 

 

  • 0

#2 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 15 Сентябрь 2010 - 21:24

gaaarfild,
1) Не CVS а CSV - (от англ. Comma Separated Values — значения, разделённые запятыми) — это текстовый формат, предназначенный для представления табличных данных.
2) Я бы сделал так (если позволяет память в PHP), все бы даннные из файлов считал бы в массивы, как, я думаю объяснять не стоит.
3) Далее сразу бы прошёлся по базе с поиском уже существующих и удалил бы из массива не нужные данные. Тут без кучи запросов не обойтись
4) А вот со вставкой можно оптимизировать. Вообще за один запрос можно вставить кучу записей, для этого просто нужно перечислять их через запятую:
INSERT INTO table VALUES (данные1), (данные2), ... , (данныеN);

Тут тебе поможет функция implode. Как видишь, тут один запрос и много записей за раз.
5) Так можно сформировать для всех INSERT, вот только я не помню, есть ли в MySQL ограничение на количество вставляемых записей за раз... Ну тут можно и разбить массив, по частям вставлять.
  • 0

#3 gaaarfild

gaaarfild
    Topic Starter
  • Пользователь
  • 596 сообщений
  • Репутация: 0

Отправлено 15 Сентябрь 2010 - 22:02

Проблема в том, что сервер вроде VPS c 2 гигами оперативы и нормальным процессором. Но при загрузке файла более 700 кб вешается все напрочь.
  • 0

#4 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 15 Сентябрь 2010 - 22:31

gaaarfild, Нужно посмотреть сколько памяти выделяется PHP-процессу:
http://www.php.su/fu...emory-get-usage

Пойми, если ты читаешь сразу весь файл, то он пытается занестись полностью в память выделенную процессу, если она мала, то это очень плохо.
Могу предложить читать файл построчно или посимвольно, намного быстрее. Тут главное чтобы памяти хватило для хранения данных.
  • 0

#5 elagin1987

elagin1987
  • Неактивированные
  • 36 сообщений
  • Репутация: 0

Отправлено 04 Октябрь 2010 - 03:27

Вот этой функцией можно читать большие файлы, проверял на 2 гиговом файле
fread

  • 0


Оформление форума – IPBSkins.ru