Tổng hợp làm việc với MySQL bằng PHP
Trần Thị Vân
- 112
- 24/07/2026
Tổng hợp kiến thức làm việc với MySQL bằng PHP từ cơ bản đến nâng cao: kết nối cơ sở dữ liệu, CRUD, JOIN, Prepared Statement, Transaction, phân trang, tối ưu truy vấn, bảo mật và kinh nghiệm thực tế, chi tiết như nào hãy cùng chúng tôi phân tích:
MySQL và PHP là bộ đôi được sử dụng rất phổ biến trong quá trình phát triển website. Hầu hết các hệ thống từ blog cá nhân, website doanh nghiệp, trang thương mại điện tử cho đến các phần mềm quản lý đều cần lưu trữ dữ liệu, truy vấn và xử lý thông tin từ cơ sở dữ liệu. Việc nắm vững cách làm việc giữa PHP và MySQL không chỉ giúp lập trình viên xây dựng ứng dụng nhanh hơn mà còn góp phần nâng cao hiệu suất, bảo mật và khả năng mở rộng của hệ thống.
Bài viết này tổng hợp đầy đủ kiến thức về cách kết nối, truy vấn, thêm, sửa, xóa dữ liệu, xử lý lỗi, tối ưu hiệu năng cũng như những kinh nghiệm thực tế khi làm việc với MySQL trong PHP. Nội dung được trình bày theo hướng dễ hiểu, phù hợp cho cả người mới học và lập trình viên muốn hệ thống lại kiến thức.

Vai trò của MySQL trong ứng dụng PHP
PHP là ngôn ngữ xử lý phía máy chủ, còn MySQL đảm nhận nhiệm vụ lưu trữ dữ liệu. Hai thành phần này phối hợp với nhau để tạo nên những website có khả năng quản lý dữ liệu động.
Ví dụ, khi người dùng đăng nhập vào website, PHP sẽ nhận dữ liệu từ biểu mẫu, gửi truy vấn đến MySQL để kiểm tra tài khoản. Sau khi MySQL trả kết quả, PHP tiếp tục xử lý và phản hồi cho người dùng.
Quy trình này diễn ra rất nhanh và được lặp lại hàng nghìn hoặc hàng triệu lần trên các hệ thống lớn.
Những dữ liệu thường được lưu trong MySQL
- Thông tin tài khoản người dùng.
- Bài viết và chuyên mục.
- Sản phẩm.
- Đơn hàng.
- Bình luận.
- Danh sách khách hàng.
- Cấu hình website.
- Nhật ký hoạt động.
Các phương thức kết nối cơ sở dữ liệu
PHP hỗ trợ nhiều cách làm việc với MySQL, tuy nhiên hiện nay chỉ nên sử dụng MySQLi hoặc PDO. Extension mysql cũ đã bị loại bỏ từ nhiều năm trước và không còn được hỗ trợ.
| Phương thức | Khuyến nghị | Đặc điểm |
|---|---|---|
| MySQLi | Nên sử dụng | Dễ học, tốc độ tốt, hỗ trợ Prepared Statement. |
| PDO | Rất nên sử dụng | Hỗ trợ nhiều hệ quản trị cơ sở dữ liệu khác nhau. |
| mysql | Không nên | Đã bị loại bỏ khỏi PHP. |
Thông tin cần chuẩn bị trước khi kết nối
Để PHP có thể giao tiếp với MySQL cần chuẩn bị đầy đủ các thông tin sau.
- Tên máy chủ cơ sở dữ liệu.
- Tên cơ sở dữ liệu.
- Tài khoản truy cập.
- Mật khẩu.
- Cổng kết nối nếu sử dụng cổng khác mặc định.
Trong môi trường localhost, máy chủ thường là localhost hoặc 127.0.0.1. Khi triển khai lên hosting hoặc VPS, các thông số này sẽ được nhà cung cấp dịch vụ cấp sẵn.
Kết nối MySQL bằng MySQLi
Đây là cách phổ biến nhất đối với các dự án PHP truyền thống.
<?php
$host = "localhost";
$user = "root";
$password = "";
$database = "demo";
$conn = new mysqli($host, $user, $password, $database);
if ($conn->connect_error) {
die("Kết nối thất bại");
}
echo "Kết nối thành công";
?>
Đoạn mã trên khởi tạo một đối tượng MySQLi và kiểm tra xem quá trình kết nối có xảy ra lỗi hay không.
Đóng kết nối sau khi hoàn thành
Việc đóng kết nối giúp giải phóng tài nguyên trên máy chủ, đặc biệt đối với các ứng dụng có lượng truy cập lớn.
<?php
$conn->close();
?>
Kết nối bằng PDO
PDO là lựa chọn được nhiều lập trình viên yêu thích vì khả năng hỗ trợ nhiều hệ quản trị cơ sở dữ liệu như MySQL, PostgreSQL, SQLite hay SQL Server.
<?php
$dsn = "mysql:host=localhost;dbname=demo;charset=utf8mb4";
try {
$pdo = new PDO($dsn, "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Không thể kết nối");
}
?>
PDO có cú pháp rõ ràng, hỗ trợ ngoại lệ và rất thuận tiện khi xây dựng các dự án lớn.
Lựa chọn bộ mã hóa ký tự phù hợp
Một trong những lỗi phổ biến nhất khi làm việc với MySQL là dữ liệu tiếng Việt bị lỗi font. Để tránh tình trạng này nên sử dụng bộ mã hóa utf8mb4 cho cả cơ sở dữ liệu, bảng dữ liệu và kết nối.
<?php
$conn->set_charset("utf8mb4");
?>
Ngoài việc thiết lập trong PHP, cơ sở dữ liệu cũng nên được tạo với collation phù hợp để đảm bảo dữ liệu luôn hiển thị chính xác.
Kiểm tra kết nối an toàn
Không nên hiển thị trực tiếp thông báo lỗi của MySQL ra ngoài trình duyệt trong môi trường thực tế vì có thể làm lộ thông tin hệ thống. Thay vào đó nên ghi lỗi vào log và chỉ hiển thị thông báo chung cho người dùng.
Trong môi trường phát triển có thể bật chế độ hiển thị lỗi để dễ kiểm tra. Khi đưa website vào hoạt động chính thức nên tắt hiển thị lỗi và lưu nhật ký để đảm bảo an toàn.
Những lỗi phổ biến khi mới làm việc với MySQL trong PHP
- Sai tên cơ sở dữ liệu.
- Nhập sai tài khoản hoặc mật khẩu.
- Máy chủ MySQL chưa khởi động.
- Không đặt đúng charset.
- Sử dụng extension mysql đã lỗi thời.
- Thiếu quyền truy cập cơ sở dữ liệu.
- Kết nối thành công nhưng truy vấn sai tên bảng.
Việc hiểu rõ nguyên nhân của từng lỗi sẽ giúp quá trình phát triển website nhanh hơn và hạn chế thời gian dò tìm sự cố.
Định hướng cho các thao tác tiếp theo
Sau khi kết nối thành công với MySQL, bước tiếp theo là thực hiện các thao tác với dữ liệu như truy vấn, thêm bản ghi, cập nhật, xóa, tìm kiếm, phân trang, sử dụng Prepared Statement, Transaction, Join, Index, tối ưu hiệu suất và các kỹ thuật bảo mật khi làm việc với cơ sở dữ liệu. Đây cũng là những nội dung quan trọng quyết định chất lượng của một ứng dụng PHP trong thực tế.
Thực hiện truy vấn lấy dữ liệu
Sau khi kết nối thành công với cơ sở dữ liệu, thao tác được sử dụng nhiều nhất là truy vấn để lấy thông tin. Tùy theo mục đích, câu lệnh SQL có thể lấy toàn bộ dữ liệu hoặc chỉ lấy những bản ghi thỏa mãn điều kiện.
Trong MySQLi, phương thức query() thường được sử dụng để gửi câu lệnh SQL đến máy chủ MySQL.
<?php
$sql = "SELECT * FROM users";
$result = $conn->query($sql);
?>
Nếu truy vấn thành công, biến $result sẽ chứa tập kết quả để tiếp tục xử lý.
Duyệt từng bản ghi
Kết quả trả về thường được đọc theo từng dòng bằng phương thức fetch_assoc(). Phương thức này trả về một mảng kết hợp, trong đó tên cột chính là khóa của mảng.
<?php
while ($row = $result->fetch_assoc()) {
echo $row["name"];
}
?>
Đây là cách phổ biến nhất khi hiển thị danh sách sản phẩm, bài viết hoặc thành viên trên website.
Lấy một bản ghi duy nhất
Khi truy vấn chỉ trả về một kết quả, có thể đọc trực tiếp mà không cần sử dụng vòng lặp.
<?php
$sql = "SELECT * FROM users WHERE id = 1";
$result = $conn->query($sql);
$user = $result->fetch_assoc();
echo $user["name"];
?>
Đếm số lượng bản ghi
Nhiều chức năng cần biết có bao nhiêu dữ liệu được trả về trước khi xử lý tiếp.
<?php
echo $result->num_rows;
?>
Việc kiểm tra số lượng bản ghi giúp tránh xử lý trên tập dữ liệu rỗng.
<?php
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo $row["name"];
}
} else {
echo "Không có dữ liệu.";
}
?>
Thêm dữ liệu mới vào bảng
Câu lệnh INSERT được sử dụng khi cần tạo thêm dữ liệu mới. Đây là thao tác thường gặp trong chức năng đăng ký thành viên, thêm sản phẩm, đăng bài viết hoặc lưu đơn hàng.
<?php
$sql = "INSERT INTO users(name,email)
VALUES('Nguyen Van A','a@example.com')";
$conn->query($sql);
?>
Sau khi thêm thành công, MySQL sẽ tạo thêm một bản ghi mới trong bảng.
Lấy ID vừa được tạo
Nếu bảng sử dụng AUTO_INCREMENT, PHP có thể lấy ngay ID của bản ghi vừa thêm.
<?php
echo $conn->insert_id;
?>
Tính năng này rất hữu ích khi cần lưu dữ liệu liên quan ở các bảng khác.
Cập nhật dữ liệu
Khi cần thay đổi thông tin đã lưu, sử dụng câu lệnh UPDATE. Đây là thao tác thường gặp trong chức năng chỉnh sửa hồ sơ, cập nhật sản phẩm hoặc thay đổi trạng thái đơn hàng.
<?php
$sql = "UPDATE users
SET name='Tran Van B'
WHERE id=1";
$conn->query($sql);
?>
Khi sử dụng UPDATE, luôn cần có điều kiện WHERE để tránh cập nhật toàn bộ dữ liệu trong bảng.
Kiểm tra số bản ghi bị ảnh hưởng
<?php
echo $conn->affected_rows;
?>
Nếu kết quả lớn hơn 0 nghĩa là đã có dữ liệu được thay đổi.
Xóa dữ liệu
DELETE được sử dụng để loại bỏ những bản ghi không còn cần thiết.
<?php
$sql = "DELETE FROM users
WHERE id=5";
$conn->query($sql);
?>
Nên hạn chế xóa trực tiếp đối với các dữ liệu quan trọng. Trong nhiều hệ thống thực tế, lập trình viên thường sử dụng trạng thái kích hoạt thay vì xóa vật lý để có thể khôi phục khi cần.
Cẩn thận khi thiếu điều kiện WHERE
Một lỗi khá phổ biến là quên điều kiện WHERE.
<?php
DELETE FROM users;
?>
Câu lệnh trên sẽ xóa toàn bộ dữ liệu trong bảng. Đây là lỗi rất nghiêm trọng và cần đặc biệt tránh trong quá trình phát triển.
Sử dụng điều kiện WHERE
WHERE giúp giới hạn dữ liệu cần xử lý. Thay vì lấy toàn bộ bảng, chỉ những bản ghi thỏa mãn điều kiện mới được trả về.
<?php
$sql = "SELECT *
FROM users
WHERE status = 1";
?>
Có thể kết hợp nhiều điều kiện để tăng độ chính xác của truy vấn.
<?php
$sql = "SELECT *
FROM users
WHERE status = 1
AND age >= 18";
?>
Sắp xếp dữ liệu với ORDER BY
ORDER BY giúp thay đổi thứ tự hiển thị dữ liệu.
| Câu lệnh | Ý nghĩa |
|---|---|
| ASC | Sắp xếp tăng dần. |
| DESC | Sắp xếp giảm dần. |
<?php
$sql = "SELECT *
FROM users
ORDER BY name ASC";
?>
Hoặc hiển thị bài viết mới nhất.
<?php
$sql = "SELECT *
FROM posts
ORDER BY id DESC";
?>
Giới hạn số lượng bản ghi với LIMIT
Không nên lấy toàn bộ dữ liệu nếu bảng có hàng chục nghìn bản ghi. LIMIT giúp giảm lượng dữ liệu trả về, cải thiện tốc độ truy vấn.
<?php
$sql = "SELECT *
FROM posts
LIMIT 10";
?>
Có thể kết hợp với OFFSET để xây dựng chức năng phân trang.
<?php
$sql = "SELECT *
FROM posts
LIMIT 20 OFFSET 40";
?>
Tìm kiếm dữ liệu bằng LIKE
LIKE được sử dụng khi cần tìm kiếm theo từ khóa.
<?php
$sql = "SELECT *
FROM products
WHERE name LIKE '%Laptop%'";
?>
Dấu phần trăm đại diện cho chuỗi ký tự bất kỳ. Đây là kỹ thuật thường được sử dụng trong chức năng tìm kiếm sản phẩm hoặc bài viết.
Sử dụng IN để lọc nhiều giá trị
Khi cần lấy nhiều giá trị cùng lúc, IN sẽ giúp câu lệnh ngắn gọn và dễ đọc hơn.
<?php
$sql = "SELECT *
FROM orders
WHERE status IN (1,2,3)";
?>
Thay vì viết nhiều điều kiện OR, IN giúp câu lệnh rõ ràng và dễ bảo trì hơn.
Lọc dữ liệu theo khoảng với BETWEEN
BETWEEN thường được dùng khi lọc theo giá tiền, ngày tháng hoặc điểm số.
<?php
$sql = "SELECT *
FROM products
WHERE price BETWEEN 1000000 AND 5000000";
?>
Câu lệnh trên sẽ lấy các sản phẩm có giá nằm trong khoảng từ một triệu đến năm triệu đồng.
Kết hợp nhiều điều kiện
Trong thực tế, truy vấn thường kết hợp nhiều điều kiện để đáp ứng yêu cầu nghiệp vụ.
<?php
$sql = "SELECT *
FROM products
WHERE category_id = 2
AND status = 1
ORDER BY created_at DESC
LIMIT 12";
?>
Đây là kiểu truy vấn thường gặp khi hiển thị danh sách sản phẩm theo danh mục trên các website bán hàng, giúp dữ liệu được lọc chính xác, sắp xếp hợp lý và chỉ lấy số lượng bản ghi cần thiết để tối ưu hiệu năng.
Kết hợp dữ liệu từ nhiều bảng bằng JOIN
Trong hầu hết các ứng dụng thực tế, dữ liệu không được lưu trong một bảng duy nhất mà được tách thành nhiều bảng để giảm trùng lặp và dễ quản lý. Khi cần lấy thông tin liên quan giữa các bảng, JOIN là kỹ thuật quan trọng cần nắm vững.
Ví dụ, bảng orders chỉ lưu mã khách hàng, còn thông tin chi tiết khách hàng được lưu trong bảng users. JOIN sẽ giúp lấy đầy đủ dữ liệu trong một lần truy vấn.
INNER JOIN
INNER JOIN chỉ trả về những bản ghi có dữ liệu tương ứng ở cả hai bảng.
<?php
$sql = "
SELECT orders.id,
users.name,
orders.total
FROM orders
INNER JOIN users
ON orders.user_id = users.id
";
$result = $conn->query($sql);
?>
LEFT JOIN
LEFT JOIN luôn lấy toàn bộ dữ liệu của bảng bên trái, ngay cả khi bảng bên phải không có dữ liệu tương ứng.
<?php
$sql = "
SELECT users.name,
orders.id
FROM users
LEFT JOIN orders
ON users.id = orders.user_id
";
?>
LEFT JOIN thường được sử dụng khi cần thống kê danh sách khách hàng, kể cả những người chưa từng đặt hàng.
Nhóm dữ liệu với GROUP BY
GROUP BY được sử dụng để gom các bản ghi có cùng giá trị thành từng nhóm. Đây là câu lệnh rất hữu ích khi thống kê dữ liệu.
<?php
$sql = "
SELECT category_id,
COUNT(*) AS total
FROM products
GROUP BY category_id
";
?>
Kết quả trả về sẽ cho biết mỗi danh mục đang có bao nhiêu sản phẩm.
Kết hợp GROUP BY với HAVING
HAVING giúp lọc kết quả sau khi dữ liệu đã được nhóm.
<?php
$sql = "
SELECT category_id,
COUNT(*) AS total
FROM products
GROUP BY category_id
HAVING total > 10
";
?>
Ví dụ trên chỉ lấy những danh mục có nhiều hơn mười sản phẩm.
Sử dụng các hàm thống kê
MySQL cung cấp nhiều hàm giúp tính toán trực tiếp trên dữ liệu mà không cần xử lý bằng PHP.
| Hàm | Mục đích |
|---|---|
| COUNT() | Đếm số bản ghi. |
| SUM() | Tính tổng. |
| AVG() | Tính giá trị trung bình. |
| MIN() | Lấy giá trị nhỏ nhất. |
| MAX() | Lấy giá trị lớn nhất. |
<?php
$sql = "
SELECT
COUNT(*) AS total_user,
MAX(age) AS max_age,
MIN(age) AS min_age,
AVG(age) AS average_age
FROM users
";
?>
Prepared Statement giúp chống SQL Injection
Một trong những lỗi bảo mật nguy hiểm nhất khi làm việc với MySQL là SQL Injection. Nguyên nhân thường do nối trực tiếp dữ liệu người dùng vào câu lệnh SQL.
Ví dụ dưới đây là cách viết không an toàn.
<?php
$username = $_POST["username"];
$sql = "SELECT * FROM users
WHERE username = '$username'";
?>
Nếu người dùng nhập dữ liệu độc hại, toàn bộ câu lệnh SQL có thể bị thay đổi.
Cách viết an toàn bằng Prepared Statement
<?php
$stmt = $conn->prepare(
"SELECT * FROM users
WHERE username = ?"
);
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
?>
Prepared Statement giúp tách dữ liệu khỏi câu lệnh SQL nên gần như loại bỏ được nguy cơ SQL Injection nếu sử dụng đúng cách.
Prepared Statement với INSERT
Kỹ thuật này cũng nên áp dụng khi thêm dữ liệu mới.
<?php
$stmt = $conn->prepare(
"INSERT INTO users(name,email)
VALUES(?, ?)"
);
$stmt->bind_param(
"ss",
$name,
$email
);
$stmt->execute();
?>
Không chỉ an toàn hơn mà đoạn mã còn dễ tái sử dụng nhiều lần.
Prepared Statement với UPDATE
<?php
$stmt = $conn->prepare(
"UPDATE users
SET email = ?
WHERE id = ?"
);
$stmt->bind_param(
"si",
$email,
$id
);
$stmt->execute();
?>
Prepared Statement với DELETE
<?php
$stmt = $conn->prepare(
"DELETE FROM users
WHERE id = ?"
);
$stmt->bind_param(
"i",
$id
);
$stmt->execute();
?>
Ý nghĩa của bind_param()
Tham số đầu tiên của bind_param() cho biết kiểu dữ liệu sẽ truyền vào.
| Ký tự | Kiểu dữ liệu |
|---|---|
| i | Số nguyên. |
| d | Số thực. |
| s | Chuỗi. |
| b | Dữ liệu nhị phân. |
Việc khai báo đúng kiểu dữ liệu giúp MySQL xử lý chính xác và tối ưu hơn.
Thực hiện Transaction
Transaction được sử dụng khi nhiều câu lệnh SQL phải thành công đồng thời. Nếu một bước xảy ra lỗi, toàn bộ thay đổi sẽ được hủy bỏ.
Đây là kỹ thuật rất quan trọng trong các hệ thống thanh toán, ngân hàng hoặc quản lý đơn hàng.
<?php
$conn->begin_transaction();
try {
$conn->query(
"UPDATE accounts
SET balance = balance - 100
WHERE id = 1"
);
$conn->query(
"UPDATE accounts
SET balance = balance + 100
WHERE id = 2"
);
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
}
?>
Nếu một trong hai câu lệnh cập nhật thất bại, dữ liệu sẽ quay về trạng thái ban đầu.
Kiểm tra lỗi truy vấn
Trong quá trình phát triển, việc kiểm tra lỗi giúp phát hiện nhanh các vấn đề liên quan đến SQL.
<?php
if (!$conn->query($sql)) {
echo $conn->error;
}
?>
Khi đưa website vào môi trường thực tế, nên ghi lỗi vào file nhật ký thay vì hiển thị trực tiếp để tránh lộ cấu trúc cơ sở dữ liệu.
Giải phóng bộ nhớ sau khi truy vấn
Sau khi xử lý xong tập kết quả lớn, nên giải phóng bộ nhớ để giảm tài nguyên sử dụng.
<?php
$result->free();
?>
Đây là thói quen tốt khi làm việc với các bảng có số lượng bản ghi lớn hoặc các trang thống kê phức tạp.
Khi nào nên sử dụng MySQLi và khi nào nên dùng PDO
Nếu dự án chỉ sử dụng MySQL và cần cú pháp đơn giản, MySQLi là lựa chọn phù hợp. Trong khi đó, PDO thích hợp với các dự án có khả năng chuyển đổi sang hệ quản trị cơ sở dữ liệu khác hoặc cần cách xử lý ngoại lệ linh hoạt hơn.
Dù lựa chọn thư viện nào, nguyên tắc quan trọng vẫn là luôn sử dụng Prepared Statement, kiểm tra lỗi cẩn thận và xây dựng câu lệnh SQL rõ ràng để đảm bảo hiệu năng cũng như tính bảo mật của ứng dụng.
Xây dựng chức năng phân trang dữ liệu
Khi bảng dữ liệu có hàng nghìn hoặc hàng triệu bản ghi, việc lấy toàn bộ dữ liệu trong một lần truy vấn sẽ làm tăng thời gian phản hồi và tiêu tốn tài nguyên máy chủ. Phân trang giúp chỉ lấy số lượng bản ghi cần thiết cho từng lần hiển thị.
Ý tưởng cơ bản là xác định trang hiện tại, số bản ghi trên mỗi trang và vị trí bắt đầu lấy dữ liệu.
Tính vị trí bắt đầu
<?php
$page = 1;
$limit = 10;
$offset = ($page - 1) * $limit;
?>
Truy vấn dữ liệu theo từng trang
<?php
$sql = "SELECT *
FROM posts
ORDER BY id DESC
LIMIT $limit OFFSET $offset";
$result = $conn->query($sql);
?>
Việc phân trang giúp website tải nhanh hơn, đồng thời mang lại trải nghiệm tốt hơn cho người dùng.
Đếm tổng số bản ghi
Để biết có bao nhiêu trang, cần đếm tổng số dữ liệu trong bảng.
<?php
$sql = "SELECT COUNT(*) AS total
FROM posts";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
$totalRecord = $row["total"];
?>
Sắp xếp dữ liệu theo nhiều cột
ORDER BY có thể kết hợp nhiều trường để tạo cách sắp xếp phù hợp với yêu cầu thực tế.
<?php
$sql = "
SELECT *
FROM products
ORDER BY category_id ASC,
price DESC
";
?>
Ví dụ trên sẽ sắp xếp theo danh mục trước, sau đó các sản phẩm trong cùng danh mục sẽ được sắp xếp theo giá giảm dần.
Sử dụng DISTINCT để loại bỏ dữ liệu trùng lặp
Trong một số trường hợp chỉ cần lấy những giá trị duy nhất, DISTINCT sẽ giúp loại bỏ các dòng bị lặp.
<?php
$sql = "
SELECT DISTINCT city
FROM users
";
?>
Đây là câu lệnh thường dùng khi lấy danh sách tỉnh thành, quốc gia hoặc danh mục không trùng nhau.
Làm việc với giá trị NULL
Trong MySQL, NULL thể hiện dữ liệu chưa có giá trị. Khi kiểm tra NULL không được sử dụng dấu bằng mà phải dùng IS NULL hoặc IS NOT NULL.
<?php
$sql = "
SELECT *
FROM users
WHERE avatar IS NULL
";
?>
Để lấy những người đã có ảnh đại diện.
<?php
$sql = "
SELECT *
FROM users
WHERE avatar IS NOT NULL
";
?>
Lấy dữ liệu theo khoảng thời gian
Các website quản trị thường cần thống kê dữ liệu theo ngày, tháng hoặc năm.
<?php
$sql = "
SELECT *
FROM orders
WHERE created_at
BETWEEN '2026-01-01'
AND '2026-12-31'
";
?>
Việc lọc dữ liệu theo thời gian rất phổ biến trong các báo cáo doanh thu, thống kê truy cập hoặc lịch sử giao dịch.
Thao tác với dữ liệu ngày tháng
MySQL hỗ trợ nhiều hàm xử lý thời gian giúp giảm lượng công việc phía PHP.
| Hàm | Mục đích |
|---|---|
| NOW() | Lấy thời gian hiện tại. |
| CURDATE() | Lấy ngày hiện tại. |
| YEAR() | Lấy năm. |
| MONTH() | Lấy tháng. |
| DAY() | Lấy ngày. |
<?php
$sql = "
SELECT *
FROM orders
WHERE YEAR(created_at)=2026
";
?>
Tìm kiếm gần đúng bằng LIKE
LIKE hỗ trợ nhiều kiểu tìm kiếm khác nhau.
| Cú pháp | Ý nghĩa |
|---|---|
| 'abc%' | Bắt đầu bằng abc. |
| '%abc' | Kết thúc bằng abc. |
| '%abc%' | Chứa abc. |
| 'a%c' | Bắt đầu bằng a và kết thúc bằng c. |
Thực hiện tìm kiếm bằng Prepared Statement
Khi dữ liệu tìm kiếm được nhập từ người dùng, nên kết hợp Prepared Statement để đảm bảo an toàn.
<?php
$keyword = "%".$keyword."%";
$stmt = $conn->prepare(
"SELECT *
FROM products
WHERE name LIKE ?"
);
$stmt->bind_param(
"s",
$keyword
);
$stmt->execute();
$result = $stmt->get_result();
?>
Xây dựng chức năng đăng nhập
Đăng nhập là ví dụ điển hình khi làm việc giữa PHP và MySQL.
- Người dùng nhập tài khoản và mật khẩu.
- PHP nhận dữ liệu từ biểu mẫu.
- Prepared Statement truy vấn tài khoản.
- So sánh mật khẩu.
- Tạo phiên đăng nhập nếu thành công.
Không nên lưu mật khẩu dưới dạng văn bản thuần mà cần sử dụng hàm băm của PHP.
Lưu mật khẩu an toàn
PHP cung cấp các hàm chuyên dụng để mã hóa và xác thực mật khẩu.
Tạo mật khẩu
<?php
$password = password_hash(
"123456",
PASSWORD_DEFAULT
);
?>
Kiểm tra mật khẩu
<?php
if (
password_verify(
$inputPassword,
$passwordHash
)
) {
echo "Đăng nhập thành công.";
}
?>
Việc sử dụng password_hash() và password_verify() giúp tăng đáng kể mức độ an toàn so với việc tự mã hóa bằng MD5 hoặc SHA1.
Upload ảnh và lưu đường dẫn vào MySQL
Thông thường, website chỉ lưu đường dẫn của tệp trong MySQL thay vì lưu trực tiếp nội dung của tệp.
<?php
$image = "uploads/avatar.jpg";
$stmt = $conn->prepare(
"UPDATE users
SET avatar=?
WHERE id=?"
);
$stmt->bind_param(
"si",
$image,
$id
);
$stmt->execute();
?>
Cách làm này giúp cơ sở dữ liệu gọn nhẹ, đồng thời dễ dàng sao lưu và quản lý tệp trên máy chủ.
Thực hiện CRUD hoàn chỉnh
CRUD là viết tắt của bốn thao tác quan trọng nhất trong quản lý dữ liệu.
| Thao tác | Ý nghĩa |
|---|---|
| Create | Thêm dữ liệu. |
| Read | Đọc dữ liệu. |
| Update | Cập nhật dữ liệu. |
| Delete | Xóa dữ liệu. |
Hầu hết mọi hệ thống quản trị nội dung, phần mềm quản lý hay website thương mại điện tử đều được xây dựng dựa trên bốn thao tác cơ bản này. Việc tổ chức mã nguồn hợp lý, kết hợp Prepared Statement và kiểm tra lỗi đầy đủ sẽ giúp chức năng CRUD hoạt động ổn định, dễ bảo trì và đảm bảo an toàn cho dữ liệu.
Tối ưu tốc độ truy vấn MySQL trong PHP
Hiệu năng của một ứng dụng không chỉ phụ thuộc vào PHP mà còn chịu ảnh hưởng rất lớn từ cách thiết kế cơ sở dữ liệu và câu lệnh SQL. Một truy vấn không được tối ưu có thể khiến website phản hồi chậm ngay cả khi máy chủ có cấu hình mạnh.
Việc tối ưu nên được thực hiện ngay từ khi thiết kế hệ thống thay vì chờ đến lúc dữ liệu phát sinh quá lớn.
Chỉ lấy những cột cần thiết
Một sai lầm phổ biến là luôn sử dụng SELECT *. Điều này khiến MySQL phải trả về toàn bộ cột mặc dù ứng dụng chỉ sử dụng một vài trường.
Không nên viết.
<?php
$sql = "
SELECT *
FROM users
";
?>
Nên viết.
<?php
$sql = "
SELECT id,
name,
email
FROM users
";
?>
Việc chỉ lấy các cột cần thiết giúp giảm dung lượng dữ liệu truyền giữa MySQL và PHP.
Luôn có điều kiện khi truy vấn
Nếu bảng chứa hàng triệu bản ghi, việc truy vấn toàn bộ dữ liệu sẽ gây áp lực lớn lên máy chủ.
<?php
$sql = "
SELECT id,
title
FROM posts
WHERE status = 1
";
?>
Sử dụng Index hợp lý
Index là một trong những yếu tố quan trọng nhất quyết định tốc độ truy vấn. Khi một cột thường xuyên được tìm kiếm hoặc dùng để JOIN, nên tạo chỉ mục cho cột đó.
Một số trường thường được tạo Index gồm:
- ID.
- Email.
- Username.
- Slug.
- Category ID.
- User ID.
- Created At.
Tuy nhiên, không nên tạo quá nhiều Index vì mỗi lần INSERT hoặc UPDATE, MySQL cũng phải cập nhật lại các chỉ mục tương ứng.
Giới hạn dữ liệu trả về
Khi xây dựng trang danh sách, chỉ nên lấy đúng số lượng bản ghi cần hiển thị.
<?php
$sql = "
SELECT id,
title
FROM posts
ORDER BY id DESC
LIMIT 20
";
?>
Không nên lấy hàng nghìn bản ghi rồi mới xử lý bằng PHP.
Tránh truy vấn lặp lại trong vòng lặp
Một lỗi thường gặp là thực hiện truy vấn mới bên trong mỗi lần lặp dữ liệu. Điều này làm tăng rất nhiều số lần giao tiếp với MySQL.
Thay vì thực hiện nhiều truy vấn nhỏ, hãy sử dụng JOIN hoặc lấy toàn bộ dữ liệu cần thiết trong một lần truy vấn.
Sử dụng EXPLAIN để phân tích truy vấn
EXPLAIN là công cụ rất hữu ích giúp phân tích cách MySQL thực hiện một câu lệnh SQL.
<?php
$sql = "
EXPLAIN
SELECT *
FROM products
WHERE category_id = 2
";
?>
Thông qua kết quả EXPLAIN, lập trình viên có thể biết truy vấn đã sử dụng Index hay chưa và phát hiện các điểm cần tối ưu.
Đóng Statement sau khi sử dụng
Khi làm việc với Prepared Statement, nên đóng Statement sau khi hoàn thành để giải phóng tài nguyên.
<?php
$stmt->close();
?>
Đóng kết nối cơ sở dữ liệu
Mặc dù PHP sẽ tự động đóng kết nối khi kết thúc chương trình, việc chủ động đóng kết nối vẫn là một thói quen tốt.
<?php
$conn->close();
?>
Xử lý ngoại lệ khi làm việc với PDO
PDO hỗ trợ cơ chế Exception giúp việc phát hiện lỗi trở nên đơn giản hơn.
<?php
try {
$stmt = $pdo->query(
"SELECT * FROM users"
);
} catch (PDOException $e) {
echo "Có lỗi xảy ra.";
}
?>
Trong môi trường thực tế, thay vì hiển thị lỗi trực tiếp, nên ghi nội dung lỗi vào hệ thống nhật ký.
Backup cơ sở dữ liệu định kỳ
Dù hệ thống được xây dựng tốt đến đâu, việc sao lưu dữ liệu vẫn là yêu cầu bắt buộc. Một bản sao lưu đầy đủ giúp giảm thiểu rủi ro khi máy chủ gặp sự cố hoặc thao tác nhầm dữ liệu.
Nên xây dựng kế hoạch sao lưu theo chu kỳ phù hợp với tần suất cập nhật dữ liệu của website.
- Sao lưu hàng ngày đối với website có nhiều giao dịch.
- Sao lưu hàng tuần đối với website ít thay đổi.
- Lưu nhiều phiên bản sao lưu.
- Lưu bản sao trên máy chủ khác.
- Kiểm tra khả năng khôi phục định kỳ.
Khôi phục dữ liệu khi cần thiết
Việc có bản sao lưu nhưng không kiểm tra khả năng phục hồi cũng tiềm ẩn nhiều rủi ro. Do đó, cần thường xuyên thử nghiệm quy trình khôi phục trên môi trường thử nghiệm trước khi áp dụng cho hệ thống thực tế.
Những lỗi thường gặp khi làm việc với MySQL bằng PHP
| Lỗi | Nguyên nhân phổ biến |
|---|---|
| Access denied | Sai tài khoản hoặc mật khẩu. |
| Unknown database | Không tồn tại cơ sở dữ liệu. |
| Table doesn't exist | Sai tên bảng. |
| Unknown column | Sai tên cột. |
| Duplicate entry | Trùng khóa duy nhất. |
| Too many connections | Quá nhiều kết nối đến MySQL. |
Kinh nghiệm thực tế khi phát triển dự án
- Luôn sử dụng Prepared Statement với dữ liệu từ người dùng.
- Không lưu mật khẩu ở dạng văn bản thuần.
- Thiết lập charset utf8mb4 ngay từ đầu.
- Thiết kế bảng dữ liệu theo nguyên tắc chuẩn hóa.
- Đặt tên bảng và cột thống nhất.
- Sử dụng Transaction với các nghiệp vụ liên quan đến tiền hoặc dữ liệu quan trọng.
- Tạo Index cho các cột thường xuyên tìm kiếm.
- Không hiển thị lỗi SQL ra ngoài website.
- Thường xuyên sao lưu cơ sở dữ liệu.
- Theo dõi hiệu năng truy vấn để tối ưu kịp thời.
Những nguyên tắc giúp ứng dụng ổn định lâu dài
Khi quy mô dự án ngày càng lớn, số lượng truy cập và dữ liệu tăng lên nhanh chóng, việc viết đúng cú pháp SQL là chưa đủ. Một hệ thống bền vững cần có cấu trúc cơ sở dữ liệu hợp lý, câu lệnh được tối ưu, cơ chế phân quyền rõ ràng và quy trình sao lưu đáng tin cậy.
Bên cạnh đó, việc kết hợp giữa PHP và MySQL nên tuân thủ các nguyên tắc về bảo mật, hạn chế truy vấn dư thừa, sử dụng Prepared Statement cho mọi dữ liệu đầu vào và thường xuyên đánh giá hiệu năng của hệ thống. Những thói quen này sẽ giúp website vận hành ổn định, dễ mở rộng và giảm đáng kể nguy cơ phát sinh lỗi trong quá trình khai thác lâu dài.
Kết luận
Làm việc với MySQL bằng PHP là nền tảng của rất nhiều ứng dụng web hiện nay. Từ những thao tác cơ bản như kết nối cơ sở dữ liệu, truy vấn, thêm, sửa và xóa dữ liệu cho đến các kỹ thuật nâng cao như JOIN, Transaction, Prepared Statement, phân trang, Index hay tối ưu truy vấn đều góp phần tạo nên một hệ thống hoạt động hiệu quả và an toàn.
Thay vì chỉ tập trung vào việc hoàn thành chức năng, lập trình viên nên xây dựng thói quen viết mã rõ ràng, tối ưu câu lệnh SQL, kiểm soát lỗi chặt chẽ và chú trọng đến bảo mật ngay từ đầu. Đây là nền tảng giúp ứng dụng PHP có khả năng mở rộng tốt, đáp ứng lượng truy cập lớn và duy trì hiệu năng ổn định trong quá trình vận hành.
- 0 Bình luận
Email, Điện thoại của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *