W tym wpisie pokażę, jak wczytać ogromny plik .sql do bazy MySQL przy pomocy czystego PHP bez limitów czasu wykonania czy pamięci. To szczególnie przydatne, gdy masz pllik z bazą danych który zajmuje (nawet po spakowaniu do ZIP) więcej niż możesz załadować w phpMyAdmin.
Przygotowanie środowiska PHP
Na początku skryptu zdejmujemy wszystkie standardowe ograniczenia PHP, które przy dużych plikach SQL niemal zawsze powodują problemy:
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
set_time_limit(0);
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 0);
ignore_user_abort(true);Co tu się dzieje:
- brak limitu czasu wykonania – import może trwać nawet kilkanaście minut
- brak limitu pamięci – PHP nie przerwie skryptu przy dużych zapytaniach
- ignore_user_abort(true) – nawet jeśli zamkniesz kartę w przeglądarce, import będzie trwał dalej
- pełne raportowanie błędów – przy problemach dokładnie widać, w którym miejscu coś poszło nie tak
Konfiguracja połączenia z bazą danych
W tym miejscu wskazujemy plik SQL oraz dane dostępowe do bazy.
Ważne:
- plik
.sqlpowinien znajdować się lokalnie na serwerze, nie być uploadowany przez formularz charset=utf8zapobiega problemom z polskimi znakami- każdy błąd SQL zatrzyma skrypt i pokaże czytelny komunikat
- nie ma „cichych” błędów podczas importu
$sqlFile = __DIR__ . '/baza.sql';
$dsn = "mysql:host=localhost;dbname=DBNAME;charset=utf8";
$user = "DBUSER";
$pass = "PASSWORD";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_LOCAL_INFILE => true
]);
Wyłączenie kluczy obcych przez PDO
$pdo->exec("SET FOREIGN_KEY_CHECKS = 0;");Dzięki temu:
- kolejność
INSERTiCREATE TABLEnie ma znaczenia - unikamy błędów typu Cannot add or update a child row
Po zakończeniu importu klucze zostaną włączone ponownie.
Czytanie pliku SQL linia po linii
Kluczowy fragment skryptu to liniowe czytanie pliku, zamiast ładowania całości do pamięci:
$handle = fopen($sqlFile, "r");
$query = "";
Plik jest czytany przy pomocy fgets(), czyli jedna linia na raz, co pozwala obsłużyć nawet bardzo duże dumpy.
Pomijanie komentarzy i zbędnych instrukcji
Podczas importu ignorujemy:
- puste linie
- komentarze SQL
- CREATE DATABASE
- USE dbname
if (trim($line) === '' || preg_match('/^(--|#)/', trim($line))) {
continue;
}
if (preg_match('/^\s*CREATE DATABASE/i', $line)) {
continue;
}
if (preg_match('/^\s*USE\s+/i', $line)) {
continue;
}Składanie i wykonywanie zapytań SQL
Zapytania SQL często zajmują wiele linii. Dlatego linie są sklejane aż do średnika:
$query .= $line;
if (substr(trim($line), -1) === ';') {
$pdo->exec($query);
$query = "";
}
To rozwiązanie:
- poprawnie obsługuje
CREATE TABLE - poprawnie obsługuje wielolinijkowe
INSERT INTO
Do tego dodajemy obsługę błędów i wyświetlanie postępu.
catch (Exception $e) {
echo "<b>Błąd w linii $lineNumber:</b> " . $e->getMessage();
echo "<pre>$query</pre>";
exit;
}
if ($executed % 100 === 0) {
echo "Wykonano $executed zapytań...<br>";
ob_flush();
flush();
}Dzięki ob_flush() i flush():
- przeglądarka odświeża output na żywo
- widać, że skrypt faktycznie pracuje
Zakończenie importu
fclose($handle);
$pdo->exec("SET FOREIGN_KEY_CHECKS = 1;");
echo "Import zakończony. Wykonano $executed zapytań.";
Podsumowanie
Ten skrypt przydał mi się już kilka razy w praktyce, gdy standardowy import przez phpMyAdmin nie dawał rady — szczególnie przy dużych dumpach baz danych, które przekraczały limity rozmiaru lub czasu wykonania. Dzięki liniowemu czytaniu pliku i zdjęciu ograniczeń PHP import przebiegał stabilnie nawet przy bardzo dużych plikach .sql.
Jeśli masz podobny problem i potrzebujesz prostego, ale skutecznego rozwiązania, ten skrypt powinien spełnić swoje zadanie.
Repozytorium na GitHubie: https://github.com/kamilwyremski/php-mysql-big-importer/
