Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. Программирование. Ступени успешной карьеры. Авторы: Кузнецов М.В., Симдянов И.В. Объектно-ориентированное программирование на PHP. Авторы: Кузнецов М.В., Симдянов И.В. PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум (новые сообщения вниз) Структурный форум

тема: Запись большого массива в БД

Сообщения:  [1-10]    [11-20]  [21-28] 

 
 автор: sim5   (30.07.2010 в 09:48)   письмо автору
 
   для: *m*   (30.07.2010 в 09:31)
 

Последняя попытка. ) Это просто пример. Он основан на том, что просто по масимальной длине некоего значения для записи получаем количество одновременно записываемых значений (строк значений). Конечно, если одно значение, это 512 байт, например, а остальные по 2 байта, то это получается "расточительный" расчет, но и в этом случае будет выгода. Можно конечно подсчитать все как в бухгалтерии, но это несколько сложнее будет выглядеть. Тут думайте сами. Запись производится исходя из установок сервера. И так:
<?
//формирование строки для MySQL запроса
function getStringForMySQL($a$n$r false) {
  
$a array_chunk($a$n);
  
array_walk($acreate_function('&$s','$s = "(\'" . 
                 implode("\',\'", array_map("mysql_real_escape_string", $s)) . 
                 "\')";'
));
  
$a implode(',',$a);
  if(
$r$a str_replace("'""`"$a);
}

//получаем установки сервера
$q mysql_query("SHOW VARIABLES LIKE 'max_allowed_packet'");
$max mysql_result($q,0,1);

echo 
"Максимальный пакет для записи (установки сервера): " $max " байт<br>";

//не нужно выбирать по максимуму, mysql ругнется, 
//так как не только буковки для записи ему свои отдаем
//поэтому устанавливаем размер пакета для записи
//округляя его кратным 1 МБ
$max -= $max 1000000;

echo 
"Выбираем максимальный пакет для записи: " $max " байт<br>";

//имена полей для записи
$fields = array('name1''name2');
//число полей
$n count($fields);

echo 
"Записываемых полей: " $n "<br>";

//формируем тест-массив
//можете сформировать любого размера для проверки
//но по числу элементов кратных числу $n
//проверте, что будет, если число элементов будет некратным
$a array_fill(064382'Эти функции дают возможность работать с массивами различными способами. 
Массивы очень удобны для хранения, обслуживания и работы с наборами переменных. Поддерживаются одно- 
и многомерные массивы, могут быть даже массивы, создаваемые пользователем.'
);

echo 
"Всего строк для записи: " count($a)/$n '<br>';

//получаем максимальную длину записываемого значения 
$len max(array_map('strlen',$a));
//получаем число элементов массива для одновременной записи
$str floor($max/$len);
//устанавливаем это число элементов кратным числу записываемых полей
if($str $n$str -= $str $n;

//здесь "по N байт каждая....", справедливо только для этого примера
echo "За один запрос будет записано строк: " $str/$n ", по " $len*$n" байт каждая, и общим объемом " .$str*$len" байт<br>";

//разбиваем массив на число одновременно записываемых значений
$a array_chunk($a$str);

echo 
"Всего запросов на запись: " count($a) . '<br><br>';

//можете просмотреть массив в результате
//echo '<pre>';
//print_r($a);

$error false;

//формируем строку имен полей для запроса
getStringForMySQL(&$fields$ntrue);

//производим запись
foreach($a as $val) {
  
//формируем строки записываемых значений для запроса
  
getStringForMySQL(&$val$n);
  
//многострочная запись одним запросом
  
$q "INSERT INTO `test` $fields VALUES $val";
  if(!
mysql_query($q)) {
    
$error mysql_error();
    break;
  }
}

echo !
$error 'Данные записаны успешно!' 'Что-то не срослось! ' $error;

Посмотрите сколько всего строк записывается, и сколько всего запросов к базе выполняется. Вот вам и ответ на ваш вопрос.

PS. Обратите внимание на вызов функции mysql_real_escape_string в функции getStringForMySQL, она взята в кавычки. Именно так и должно быть прописано, ранее я просто упустил из виду это, хотя работать будет, и вы не увидете предупреждения, если они подавляются.

  Ответить  
 
 автор: *m*   (30.07.2010 в 09:31)   письмо автору
 
   для: sim5   (29.07.2010 в 18:44)
 

понять то я понимаю) ,а вот сделать, к сожалению, пока не могу сам..
а почему ужас? вот например 20 тыс. строк записалось за 30 сек.. или дело в другом?

  Ответить  
 
 автор: sim5   (29.07.2010 в 18:44)   письмо автору
 
   для: *m*   (29.07.2010 в 16:50)
 

Конечно можно, вы только не поймете одной вещи - на сколько разбить ваш массив, чтобы его N элементов для будующих M строк записи не выходили за пределы установленного в настройках MySQL по умолчанию. И сделать это можно даже на глаз. Ваши данные, это скорее всего результат парсинга html-страниц, а даже в сотни КБ текста трудно найти страницу, а тем более много элементов с одной страницы по сотни КБ. Из этого следует, что вы даже без РНР расчетов можете узнать "безопасное" число строк для записи, а следовательно и разбиение массива на N элементов.
Записывать же массив из многих тысяч строк отдельным запросом для каждой, это ужас.

  Ответить  
 
 автор: *m*   (29.07.2010 в 16:50)   письмо автору
 
   для: sms-send   (29.07.2010 в 14:14)
 

да нее такой цели нет, я просто хотел использовать пример sim5.
ну вобщем сделал так:


<?
foreach ($data as $index=>$val)
mysql_query("INSERT INTO `data` (`name1`, `name2`) VALUES ('$val[0]','$val[1]')");
?>


видимо об этом мне говорил Трианон, а я его не понимал и продолжал смотреть на пример sim5.
И думал как из примера sim5 можно сделать построчную запись)

  Ответить  
 
 автор: sms-send   (29.07.2010 в 14:14)   письмо автору
 
   для: *m*   (29.07.2010 в 13:46)
 

В цикле, в котором собираете запрос, ставьте проверку, если текущая длина запроса + длина добавляемого куска больше чем max_allowed_packet, то отправляйте собранный запрос в СУБД и продолжайте собирать следующий запрос.
Можно отправлять запрос на вставку каждого ряда отдельно или цель именно в том, чтобы уложить максимум данных в каждый запрос?

  Ответить  
 
 автор: *m*   (29.07.2010 в 13:46)   письмо автору
 
   для: sim5   (29.07.2010 в 12:50)
 

а у меня данные не фиксированные, то есть у меня есть одно поле текстовое, там содержится описание и оно бывает самым разным... перечитал тему вы приводили пример действительно исходя из предположения что данные фиксированные... А как же быть если данные не фиксированные?

  Ответить  
 
 автор: sim5   (29.07.2010 в 12:50)   письмо автору
 
   для: *m*   (29.07.2010 в 12:15)
 

The server's default max_allowed_packet value is 1MB. Вы не то прочли.

Можно все что угодно объеденить, был бы только смысл. В данном случае его нет, ибо нужно проходить циклом разбитый массив на N строк для записи, отдавая его в функцию. Если ваши данные это фактически фиксированные данные, то есть, например, вы записываете, к примеру, 4 поля в таблицу, и все эти четыре поля VARCHAR, при этом вы их ограничили по длине, например 100 символов, то не сложно посчитать число сток записи, которые не будут выходить за пределы установки по умолчанию. Другими словами вы знаете некую постоянную величину, и расчетов практически не потребуется.
Другое дело, если вы, например, записываете эти же четыре поля, но одно из них TEXT, и в это поле поступают данные пусть от textarea, но в одной строке это будет 30 слов, а в другой уже на несколько килобайт, и т.п.. В данном случае объем данных от строки к строке может быть различным и очень, значит нужно будет производить некий расчет и группировать число строк записи в цикле исходя из объема данных.
Коли вы знаете что за данные вам поступают, и это не произвольные по длине данные, а имеют некий максимум, то в этом случае тоже не сложно просто в уме посчитать на какое число строк нужно разбить массив, по максимальному значению. Главное чтобы это число было кратно числу записываемых полей. Далее в цикле отдавайте по частям этот массив для преобразования в строку для многострочной записи, и записывайте эту строку.

  Ответить  
 
 автор: *m*   (29.07.2010 в 12:15)   письмо автору
 
   для: sim5   (29.07.2010 в 06:57)
 

да, только я там пример приводил, небольшой, простой..
а на деле видите, сколько еще моментов.. о которых я просто не знал..
а можно ваш пример по делению массива объединить в одну функцию getStringForMySQL?

то есть наибольшим пакетом, который может быть передан к или от MySQL сервера и клиента, является - 1 GB.
Правильно ли расширять до такого объема max_allowed_packet?
Или правильно все таки делить большой массив в PHP?

  Ответить  
 
 автор: sim5   (29.07.2010 в 06:57)   письмо автору
 
   для: *m*   (27.07.2010 в 15:25)
 

http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html

В какой момент разделить? А что вы записываете, те данные которые вы в РНР разделе приводили?

  Ответить  
 
 автор: Trianon   (28.07.2010 в 02:12)   письмо автору
 
   для: *m*   (27.07.2010 в 15:31)
 

потому что если бы Вы записывали данные по одной строке на запрос, эта ошибка не возникла бы.

  Ответить  

Сообщения:  [1-10]    [11-20]  [21-28] 

Форум разработан IT-студией SoftTime
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования