Як у PDO перейти з MySQL на PostgreSQL

PDO (PHP Data Objects) дозволяє змінити базу даних з MySQL на PostgreSQL без переписування PHP-коду, якщо код написаний правильно. Однак потрібно розуміти, що саме змінюється, а що — ні.


1. Головне правило

PDO прибирає залежність від драйвера БД, але не від SQL-діалекту.

  • ✔ PHP-код зазвичай не змінюється
  • ✔ змінюється DSN підключення
  • ❌ SQL-запити та структура БД можуть потребувати адаптації

2. Заміна DSN (обовʼязково)

Було: MySQL

$pdo = new PDO(
    "mysql:host=localhost;dbname=test_db;charset=utf8mb4",
    "db_user",
    "db_pass",
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]
);

Стало: PostgreSQL

$pdo = new PDO(
    "pgsql:host=localhost;port=5432;dbname=test_db",
    "db_user",
    "db_pass",
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]
);

Весь інший PHP-код залишається без змін.


3. PDO-код, який працює і в MySQL, і в PostgreSQL

$stmt = $pdo->prepare(
    "SELECT id, name FROM users WHERE active = :active"
);

$stmt->execute([
    'active' => true
]);

$rows = $stmt->fetchAll();

4. Що ламається при переході (і як виправити)

LIMIT offset, count

MySQL:

LIMIT 10, 20

PostgreSQL:

LIMIT 20 OFFSET 10

Універсальний варіант:

LIMIT :limit OFFSET :offset

AUTO_INCREMENT

MySQL:

id INT AUTO_INCREMENT

PostgreSQL:

id SERIAL
-- або
id INT GENERATED ALWAYS AS IDENTITY

PHP-код не змінюється, якщо використовується lastInsertId().


INSERT IGNORE

MySQL:

INSERT IGNORE INTO users (...)

PostgreSQL:

INSERT INTO users (...)
ON CONFLICT DO NOTHING

ON DUPLICATE KEY UPDATE

MySQL:

ON DUPLICATE KEY UPDATE name = VALUES(name)

PostgreSQL:

ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name

IF()

MySQL:

IF(active = 1, 'yes', 'no')

PostgreSQL:

CASE WHEN active THEN 'yes' ELSE 'no' END

5. Типи даних, які потрібно змінити

MySQL PostgreSQL
TINYINT(1) BOOLEAN
DATETIME TIMESTAMP
LONGTEXT TEXT
DOUBLE DOUBLE PRECISION

6. JSON — різний синтаксис

MySQL:

JSON_EXTRACT(data, '$.name')

PostgreSQL:

data->>'name'

7. Рекомендовані налаштування PDO

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false
];

8. Коли можна змінити БД без змін PHP

  • ✔ використовується PDO
  • ✔ немає MySQL-специфічного SQL
  • ✔ відсутні FULLTEXT, ENUM, REPLACE INTO

9. Висновок

Перехід з MySQL на PostgreSQL у PDO — це перш за все:

  • 🔹 заміна DSN
  • 🔹 адаптація SQL-запитів
  • 🔹 міграція структури БД

PDO значно спрощує міграцію, але не є повною абстракцією від SQL.

Рекомендація: завжди пишіть портований SQL, щоб у майбутньому легко змінювати СУБД.

Як використовувати цикл for з PDO SELECT у PHP

PDO не дозволяє напряму перебирати результат запиту циклом for. Але цикл for можна використовувати, якщо попередньо отримати дані у вигляді масиву.


1. Основна ідея

Алгоритм такий:

  1. Виконати SELECT через PDO
  2. Отримати всі рядки методом fetchAll()
  3. Перебрати масив циклом for

2. Простий приклад SELECT + for

$stmt = $pdo->query(
    "SELECT id, name, email FROM users"
);

$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

for ($i = 0; $i < count($rows); $i++) {
    echo $rows[$i]['id'] . ' - ';
    echo $rows[$i]['name'] . ' - ';
    echo $rows[$i]['email'] . '<br>';
}

3. Оптимізований варіант (рекомендовано)

Щоб не викликати count() на кожній ітерації, краще зберегти кількість елементів у змінну:

$total = count($rows);

for ($i = 0; $i < $total; $i++) {
    echo $rows[$i]['name'] . '<br>';
}

4. SELECT з параметрами + for

$stmt = $pdo->prepare(
    "SELECT id, name FROM users WHERE active = :active"
);

$stmt->execute([
    'active' => 1
]);

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

for ($i = 0, $c = count($users); $i < $c; $i++) {
    echo $users[$i]['id'] . ' - ';
    echo $users[$i]['name'] . '<br>';
}

5. Поширена помилка (НЕПРАВИЛЬНО)

for ($i = 0; $row = $stmt->fetch(); $i++) {
    // ❌ так робити не можна
}

Метод fetch() повертає false, а цикл for не призначений для такої логіки.


6. Кращі альтернативи циклу for

while (рекомендовано для великих вибірок)

while ($row = $stmt->fetch()) {
    echo $row['name'] . '<br>';
}

foreach (найчистіший код)

foreach ($users as $row) {
    echo $row['name'] . '<br>';
}

7. Коли варто використовувати for

  • ✔ Потрібен індекс елемента
  • ✔ Дані вже у масиві
  • ✔ Є логіка, залежна від позиції

Коли НЕ варто

  • ❌ Для великих SELECT-запитів
  • ❌ Коли не потрібен індекс

8. Висновок

Цикл for можна використовувати з PDO лише після fetchAll(). У більшості випадків foreach або while будуть простішими та ефективнішими.

Рекомендація: використовуйте for тільки тоді, коли це дійсно виправдано.

Повний гайд: як користуватися PDO в PHP

PDO (PHP Data Objects) — це сучасний, безпечний та універсальний спосіб роботи з базами даних у PHP. PDO підтримує MySQL, PostgreSQL, SQLite та інші СУБД і дозволяє писати портований код без прив’язки до конкретної бази.

У цій статті ви навчитеся користуватися PDO на практиці: від підключення до бази даних до виконання складних запитів і транзакцій.


1. Чому варто використовувати PDO

  • ✔ Захист від SQL-інʼєкцій
  • ✔ Prepared statements
  • ✔ Єдиний API для різних СУБД
  • ✔ Підтримка транзакцій
  • ✔ Зручна обробка помилок

2. Підключення до бази даних через PDO

<?php
$dsn  = "mysql:host=localhost;dbname=test_db;charset=utf8mb4";
$user = "db_user";
$pass = "db_password";

try {
    $pdo = new PDO($dsn, $user, $pass, [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
    ]);
} catch (PDOException $e) {
    die("Помилка підключення: " . $e->getMessage());
}

3. SELECT-запити

Простий SELECT

$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

SELECT з параметрами (prepared statement)

$stmt = $pdo->prepare(
    "SELECT * FROM users WHERE email = :email"
);

$stmt->execute([
    'email' => '[email protected]'
]);

$user = $stmt->fetch();

4. INSERT, UPDATE, DELETE

INSERT

$stmt = $pdo->prepare(
    "INSERT INTO users (name, email)
     VALUES (:name, :email)"
);

$stmt->execute([
    'name'  => 'Ivan',
    'email' => '[email protected]'
]);

$lastId = $pdo->lastInsertId();

UPDATE

$stmt = $pdo->prepare(
    "UPDATE users SET name = :name WHERE id = :id"
);

$stmt->execute([
    'name' => 'Petro',
    'id'   => 5
]);

DELETE

$stmt = $pdo->prepare(
    "DELETE FROM users WHERE id = :id"
);

$stmt->execute([
    'id' => 10
]);

5. Плейсхолдери в PDO

Іменовані

WHERE id = :id

Позиційні

WHERE id = ?
$stmt->execute([5]);

6. Типи даних та bindValue

Для числових значень, LIMIT та OFFSET рекомендується явно вказувати тип:

$stmt = $pdo->prepare(
    "SELECT * FROM users
     WHERE active = :active
     LIMIT :limit OFFSET :offset"
);

$stmt->bindValue(':active', true, PDO::PARAM_BOOL);
$stmt->bindValue(':limit', 20, PDO::PARAM_INT);
$stmt->bindValue(':offset', 0, PDO::PARAM_INT);

$stmt->execute();

7. Отримання результатів

  • fetch() — один рядок
  • fetchAll() — всі рядки
  • rowCount() — не рекомендовано для SELECT

8. Транзакції

try {
    $pdo->beginTransaction();

    $pdo->exec(
        "UPDATE accounts
         SET balance = balance - 100
         WHERE id = 1"
    );

    $pdo->exec(
        "UPDATE accounts
         SET balance = balance + 100
         WHERE id = 2"
    );

    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    echo "Помилка транзакції";
}

9. Обробка помилок

try {
    $pdo->query("SELECT * FROM unknown_table");
} catch (PDOException $e) {
    echo $e->getMessage();
}

10. Типові помилки при роботі з PDO

  • ❌ Конкатенація змінних у SQL
  • ❌ Відсутність prepared statements
  • ❌ ATTR_EMULATE_PREPARES = true
  • ❌ rowCount() для SELECT

11. PDO у WordPress

Хоча WordPress використовує $wpdb, PDO можна застосовувати:

  • у власних плагінах
  • у REST API
  • у CLI-скриптах

12. Висновок

PDO — це стандарт для сучасного PHP. Використання prepared statements, транзакцій та портованого SQL робить код безпечним, масштабованим і готовим до майбутніх змін.

Рекомендація: завжди використовуйте PDO у нових PHP-проєктах.

Як в MySQL видалити дані з таблиці SQL запитом

У MySQL можна повністю очистити таблицю за допомогою двох основних команд:

1. DELETE (повільний, зберігає структуру та AUTO_INCREMENT)

DELETE FROM table_name;

Видаляє всі рядки, але зберігає структуру таблиці.Якщо в таблиці є AUTO_INCREMENT, лічильник не скидається.Для прискорення можна використати DELETE FROM table_name WHERE 1=1;

2. TRUNCATE TABLE (швидкий, скидає AUTO_INCREMENT)

TRUNCATE TABLE table_name;

Швидший за DELETE, бо не записує операції для кожного рядка в лог транзакцій.Скидає AUTO_INCREMENT до 1.Не можна використовувати з таблицями, що мають зовнішні ключі (FOREIGN KEY), якщо FOREIGN_KEY_CHECKS=1.

Який варіант вибрати?

  • Якщо потрібно просто очистити дані та зберегти AUTO_INCREMENT – DELETE.
  • Якщо потрібна швидкість і можна скинути AUTO_INCREMENT – TRUNCATE.

Якщо ж потрібно видалити саму таблицю разом із її структурою:

DROP TABLE table_name;

Ця команда повністю видалить таблицю, тому потім доведеться створювати її заново.

Якщо таблиця має зовнішні ключі, перед TRUNCATE чи DELETE потрібно вимкнути перевірку:

SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE table_name;
SET FOREIGN_KEY_CHECKS=1;

Ось кілька прикладів PHP-коду, що виконують очищення таблиці в MySQL за допомогою DELETE, TRUNCATE і DROP TABLE.

1. Використання DELETE

<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "test_db";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Помилка підключення: " . $conn->connect_error);
}

$sql = "DELETE FROM table_name";  // Видаляє всі дані, але зберігає структуру
if ($conn->query($sql) === TRUE) {
    echo "Дані успішно видалені!";
} else {
    echo "Помилка: " . $conn->error;
}

$conn->close();
?>

2. Використання TRUNCATE TABLE (швидше, скидає AUTO_INCREMENT)

<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "test_db";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Помилка підключення: " . $conn->connect_error);
}

$sql = "TRUNCATE TABLE table_name";  // Швидко очищає таблицю
if ($conn->query($sql) === TRUE) {
    echo "Таблиця очищена!";
} else {
    echo "Помилка: " . $conn->error;
}

$conn->close();
?>

3. Використання DROP TABLE (видаляє таблицю повністю)

<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "test_db";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Помилка підключення: " . $conn->connect_error);
}

$sql = "DROP TABLE table_name";  // Видаляє таблицю повністю
if ($conn->query($sql) === TRUE) {
    echo "Таблиця видалена!";
} else {
    echo "Помилка: " . $conn->error;
}

$conn->close();
?>

Який варіант вибрати?

  • DELETE – якщо потрібно очистити всі записи, зберігаючи AUTO_INCREMENT.
  • TRUNCATE TABLE – якщо потрібно швидко очистити таблицю і скинути AUTO_INCREMENT.
  • DROP TABLE – якщо потрібно повністю видалити таблицю.

💡 Якщо таблиця має FOREIGN KEY, перед TRUNCATE або DELETE додайте:

$conn->query("SET FOREIGN_KEY_CHECKS=0");
$conn->query("TRUNCATE TABLE table_name");
$conn->query("SET FOREIGN_KEY_CHECKS=1");