Работа с базами данных в PHP: таблицы и запросы
Работа с таблицами SQL в PHP
Взаимодействие с реляционными базами данных (MySQL, PostgreSQL, SQLite) из PHP происходит через выполнение SQL-запросов: создание таблиц, вставка, выборка, обновление и удаление записей. Для безопасной и эффективной работы используются специальные расширения: PDO или MySQLi. Ниже рассмотрены основные подходы с примерами кода и разбором типичных проблем.
Как выполнять SQL-запросы к таблицам максимально безопасно и удобно?
Самым универсальным решением считается PDO (PHP Data Objects). Оно поддерживает множество СУБД и использует подготовленные запросы, которые защищают от SQL-инъекций. Подключение к MySQL выглядит так:
<?php
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$user = 'root';
$pass = '';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
echo 'Подключение не удалось: ' . $e->getMessage();
exit;
}
// Создание таблицы
$pdo->exec('CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)');
// Подготовленный запрос для вставки
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute(['name' => 'Иван Петров', 'email' => 'ivan@example.com']);
// Выборка с параметром
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => 1]);
$user = $stmt->fetch();
echo $user['name'];
?>
Php class sql (класс для работы с sql в php)
Типичные проблемы:
- Ошибка подключения из-за неверного DSN, имени пользователя или пароля. Решение: проверять данные, использовать блок try-catch.
- Исключение PDOException при синтаксической ошибке в SQL. Решение: включать режим исключений (ERRMODE_EXCEPTION) и читать сообщение.
- Проблемы с кодировкой (например, русские символы отображаются как '???'). Решение: указывать charset=utf8mb4 в DSN.
- Неявное эмуляция подготовленных запросов может приводить к инъекциям в некоторых версиях. Решение: отключать эмуляцию (ATTR_EMULATE_PREPARES = false).
Как работать с MySQL через объектно-ориентированный интерфейс MySQLi?
Расширение MySQLi в ООП-стиле также поддерживает подготовленные запросы. Пример подключения и создания таблицы:
<?php
$mysqli = new mysqli('localhost', 'root', '', 'testdb');
if ($mysqli->connect_error) {
die('Ошибка подключения: ' . $mysqli->connect_error);
}
$mysqli->set_charset('utf8mb4');
// Создание таблицы
$sql = 'CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL
) ENGINE=InnoDB';
if (!$mysqli->query($sql)) {
echo 'Ошибка создания таблицы: ' . $mysqli->error;
}
// Подготовленная вставка
$stmt = $mysqli->prepare('INSERT INTO products (title, price) VALUES (?, ?)');
$stmt->bind_param('sd', $title, $price);
$title = 'Ноутбук';
$price = 59999.99;
$stmt->execute();
$stmt->close();
// Выборка
$result = $mysqli->query('SELECT * FROM products');
while ($row = $result->fetch_assoc()) {
echo $row['title'] . ' - ' . $row['price'] . '<br>';
}
$mysqli->close();
?>
Php sql insert (insert в php)
Возможные ошибки:
- Забыли установить кодировку через set_charset – русские символы могут не сохраниться.
- Ошибка в bind_param: неверный тип ('s' для строки, 'd' для double, 'i' для integer). Несовпадение типов приводит к пустым значениям.
- Не проверяется результат query – если запрос не удался, mysqli->error содержит описание.
Как использовать процедурный стиль MySQLi?
Процедурный вариант удобен для быстрых скриптов, но менее нагляден при сложной логике. Пример:
<?php
$link = mysqli_connect('localhost', 'root', '', 'testdb');
if (!$link) {
die('Ошибка подключения: ' . mysqli_connect_error());
}
mysqli_set_charset($link, 'utf8mb4');
$result = mysqli_query($link, 'SELECT * FROM users');
while ($row = mysqli_fetch_assoc($result)) {
echo $row['name'] . '<br>';
}
mysqli_free_result($result);
mysqli_close($link);
?>
Php ms sql (работа с ms sql в php)
Частые затруднения:
- Путаница между функциями: mysqli_query, mysqli_fetch_array, mysqli_fetch_assoc – легко ошибиться в имени.
- Не вызывается mysqli_free_result для освобождения памяти при больших результатах.
- Подготовленные запросы в процедурном стиле громоздкие (mysqli_stmt_bind_param), что повышает вероятность ошибок.
Как работать с SQLite через PDO?
SQLite – файловая база данных, идеальна для небольших проектов. Подключение и создание таблицы:
<?php
$dsn = 'sqlite:/path/to/database.sqlite';
$pdo = new PDO($dsn);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)');
$stmt = $pdo->prepare('INSERT INTO notes (content) VALUES (:content)');
$stmt->execute(['content' => 'Привет, SQLite!']);
$rows = $pdo->query('SELECT * FROM notes')->fetchAll();
foreach ($rows as $row) {
echo $row['content'] . '<br>';
}
?>
Специфические проблемы:
- Файл базы данных должен быть доступен для записи веб-серверу. Ошибка 'unable to open database file' – проверить права.
- SQLite не поддерживает FOREIGN KEY по умолчанию (требуется PRAGMA foreign_keys = ON).
- Одновременная запись может блокироваться – SQLite не рассчитан на высокие нагрузки.
Дополнительные примеры и углублённые приёмы
Ниже приведены расширенные сценарии, которые часто встречаются при работе с таблицами SQL в PHP.
<?php
// Пример 1: Транзакции в PDO
$pdo->beginTransaction();
try {
$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 'Ошибка транзакции: ' . $e->getMessage();
}
?>
Результат: баланс переведён со счёта 1 на счёт 2. Если одно из обновлений не удалось, изменения откатываются.
<?php
// Пример 2: Выборка с JOIN
$sql = 'SELECT u.name, o.amount, o.order_date
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.status = :status';
$stmt = $pdo->prepare($sql);
$stmt->execute(['status' => 'active']);
$orders = $stmt->fetchAll();
foreach ($orders as $order) {
echo $order['name'] . ' - ' . $order['amount'] . ' - ' . $order['order_date'] . '<br>';
}
?>
Пример вывода: Иван Петров - 1500.00 - 2025-03-15 Мария Смирнова - 2300.50 - 2025-03-14
<?php
// Пример 3: Вставка нескольких строк за один запрос (PDO)
$pdo->beginTransaction();
$stmt = $pdo->prepare('INSERT INTO products (title, price) VALUES (:title, :price)');
$products = [
['title' => 'Книга', 'price' => 499.00],
['title' => 'Ручка', 'price' => 89.50],
['title' => 'Тетрадь', 'price' => 120.00],
];
foreach ($products as $product) {
$stmt->execute($product);
}
$pdo->commit();
echo 'Добавлено записей: ' . count($products);
?>
Результат: в таблицу products добавлено 3 строки.
<?php
// Пример 4: Обновление с проверкой количества затронутых строк
$stmt = $pdo->prepare('UPDATE users SET name = :new_name WHERE id = :id');
$stmt->execute(['new_name' => 'Пётр Иванов', 'id' => 10]);
if ($stmt->rowCount() === 0) {
echo 'Пользователь с id=10 не найден.';
} else {
echo 'Имя обновлено.';
}
?>
Вывод: 'Имя обновлено.' или 'Пользователь с id=10 не найден.'
<?php
// Пример 5: Использование lastInsertId
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute(['Анна', 'anna@test.com']);
$newId = $pdo->lastInsertId();
echo 'Новый ID: ' . $newId;
?>
Вывод: 'Новый ID: 42' (зависит от автоинкремента).
<?php
// Пример 6: Постраничная выборка (LIMIT и OFFSET)
$page = 2;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare('SELECT * FROM articles ORDER BY id DESC LIMIT :limit OFFSET :offset');
$stmt->bindValue('limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue('offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll();
?>
Результат: на странице 2 отобразятся статьи с 11 по 20.
<?php
// Пример 7: Удаление с проверкой существования
$stmt = $pdo->prepare('DELETE FROM temp_data WHERE expires < NOW()');
$stmt->execute();
echo 'Удалено строк: ' . $stmt->rowCount();
?>
Вывод: 'Удалено строк: 5' (если просроченных записей было 5).
Эти примеры демонстрируют типовые операции: транзакции, связи между таблицами, массовые вставки, проверки, пагинацию и очистку данных. Каждый блок кода сопровождается комментариями, облегчающими понимание.