Все ленты — последние статьи

Из Firebird-Interbase в MySQL

Кажется, что может быть проще перегона данных 1 к 1 из одной релятивной базы в другую? Просто делаеш селект из одной и инсерт в другую. Но тут возникает проблемма с тем, как выбрать например записи с 900 до 1000. Большенство програмистов сразу же предложат запрос SELECT FIRST 1000 SKIP 900 …. (так как в firebird нет LIMIT) и такой запрос выполнится очень быстро, но только при маленьких объёмах данных (до 1 млн. записей). А что делать в случае, если нам необходимо сделать выборку 10 последних строк из 10 000 000 записей? Такой запрос будет выполняться уже около 52 секунд.
Как вы уже могли догадаться, есть и другой метод выборки записей — это выборка по индексу. В данной статье именно такой алгоритм я и хочу описать.

Для подключения к базам данных воспользуемся стандартными регулярными выражениями php.
Для работы нам понадобытся библиотеки php5-ibase и php5-mysql.
Описание самого процесса перегона базы я вставлю в коментарии в коде скрипта.
Предположим, что у нас есть 2 одинаковые по структуре базы данных firebird и mysql. они имеют одну таблицу с полнями
id, key, value где в обеих таблицах на поля кинуты индексы а на поле key кинут уникальный индекс. Таблицу назовём my_tbl
Вот пример кода:
<?php
ini_set('disply_error', E_ALL);
 
//Сначала зададим параметры баз данных согласто документации
$fb_host = 'localhost:база';
$fb_username='SYSDBA';
$fb_password='пароль';
$mysql_host = 'localhost';
$mysql_username='root';
$mysql_password='пароль';
$mysql_db='база';
 
// далее нам необходимо подключиться к базам данных
 
$dbh = ibase_connect ( $fb_host, $fb_username, $fb_password ) or die ("Firebird умер");
$dbm = mysql_connect ( $mysql_host,$mysql_username,$mysql_password) or die("MySQL умерла");
$tr = ibase_trans (IBASE_NOWAIT, IBASE_READ, IBASE_CONCURRENCY, $dbh);
//выбираем БД и кодировку символов. Обратите внимание, на то, что в фаербёде
//база данных выбирается иначе чем в mysql, в фаербёде она указывается при
//открытии стэка(подключении)
mysql_select_db($mysql_db);
mysql_query("SET NAMES 'cp1251'");
 
//подсчитаем общее количество записей в исходной БД
$stmt = 'select count(*) from my_tbl';
$sth = ibase_query($dbh, $stmt);
while ($row = ibase_fetch_assoc($sth))
 {
 $count = intval($row['COUNT']) ;
 }
 
//теперь нам нужно соответственно установить начальное и конечное положение индекса
//так как мы будем выбирать с нуля то $mincnt будет равно 0
$mincnt=0;
 
//определяем максимальное число уникальных строк
$stmt = 'select max(key) from my_tbl';
$sth = ibase_query($dbh, $stmt);
while ($row = ibase_fetch_assoc($sth))
 {
 $maxcnt = intval($row['MAX']);
 }
$cnt=$mincnt;
$rcnt=0; $rcnts=0;
 
//начинаем переборку записей и выборку из индекса по 50000 записей вставляя их в mysql
while ($rcnts<$count)
 {
 $stmt = 'select count(*) from my_tbl where key BETWEEN (' .$cnt. ') AND (' .($cnt+49999). ') ';
 $sth = ibase_query($tr,$stmt);
 while ($row = ibase_fetch_assoc($sth))
 {
 $rcnt = intval($row['COUNT']);
 }
  
 $stmt = 'SELECT * FROM my_tbl WHERE key BETWEEN (' .$cnt. ') AND (' .($cnt+49999). ') ORDER BY key';
 $sth = ibase_query($tr, $stmt);

Обратите внимание на то, как осушествляется перебор по индексу. мы берём из индекса кусками строки и потом генерируем обин пакет из общего числа строк. Кстати, запрос $stmt = ‘SELECT * FROM my_tbl WHERE key BETWEEN (‘ .$cnt. ‘) AND (‘ .($cnt+49999). ‘) ORDER BY key’; может вернуть нам не обязательно 50 000 строк, именно по этому мы и делаем проверку циклом while ($rcnts<$count) то есть итерации будут происходить до тех пор, пока количество выбранных из индекса строк не достигнет общего количества строк.

Данный способ является наиболее эфективным и быстродейственным. По сравнительным результатам вариант конвертирования через first x skip y на 10 000 000 записей занял бы неделю, а написанный мною вариант конвертирует базу за 30 минут и соответственно эфективнее первого метода в 336 раз.