Tại sao cần Cơ sở dữ liệu quan hệ? Ôn lại SQL
Vấn đề với lưu trữ trong bộ nhớ
Trong Bài 2 và 3, chúng ta lưu dữ liệu trong HashMap và ArrayList của Java. Điều này hoạt động cho việc học, nhưng có lỗi nghiêm trọng: tất cả dữ liệu mất khi ứng dụng khởi động lại. Hãy thử — dừng ứng dụng Spring Boot, khởi động lại, và tất cả bài viết và người dùng bạn đã tạo đều biến mất.
Cơ sở dữ liệu giải quyết điều này bằng cách lưu dữ liệu bền vững trên đĩa. Dù ứng dụng bị crash, server khởi động lại, hay bạn triển khai phiên bản mới, dữ liệu vẫn tồn tại.
Tại sao Cơ sở dữ liệu quan hệ?
Có hai loại chính:
Cơ sở dữ liệu quan hệ (SQL) lưu dữ liệu trong bảng với hàng và cột, giống bảng tính. Các bảng có thể liên kết qua quan hệ. Ví dụ: MariaDB, PostgreSQL, MySQL, Oracle, SQL Server.
Cơ sở dữ liệu phi quan hệ (NoSQL) lưu dữ liệu ở nhiều định dạng — tài liệu (MongoDB), cặp key-value (Redis), đồ thị (Neo4j), hoặc cột (Cassandra).
Cho ứng dụng blog, cơ sở dữ liệu quan hệ là lựa chọn tự nhiên vì:
- Dữ liệu blog có cấu trúc cao — bài viết có tiêu đề, nội dung, tác giả, ngày tháng. Chúng phù hợp gọn gàng với cột bảng.
- Quan hệ xuất hiện khắp nơi — người dùng có nhiều bài viết, bài viết có nhiều bình luận. Cơ sở dữ liệu quan hệ được xây dựng cho điều này.
- Tính toàn vẹn dữ liệu quan trọng — bạn không muốn bình luận trỏ đến bài viết không tồn tại. Cơ sở dữ liệu quan hệ thực thi các quy tắc này bằng khóa ngoại (foreign key).
- SQL là kỹ năng phổ quát — học một lần cho phép bạn làm việc với bất kỳ cơ sở dữ liệu quan hệ nào.
Tổng quan MariaDB — Lịch sử, Tính năng, Tương thích MySQL
Câu chuyện MariaDB
MariaDB có nguồn gốc thú vị. Năm 1995, Michael “Monty” Widenius đồng sáng tạo MySQL, trở thành cơ sở dữ liệu mã nguồn mở phổ biến nhất thế giới. Năm 2008, Sun Microsystems mua lại MySQL. Năm 2010, Oracle mua lại Sun Microsystems — và cùng với nó, MySQL.
Nhiều người trong cộng đồng mã nguồn mở lo ngại về sự quản lý MySQL của Oracle. Vì vậy Monty fork MySQL và tạo ra MariaDB năm 2009, đặt tên theo con gái nhỏ Maria (MySQL được đặt theo con gái lớn My).
MariaDB là fork được cộng đồng phát triển từ MySQL. Nó được thiết kế để thay thế hoàn toàn — nghĩa là bạn có thể chuyển từ MySQL sang MariaDB với ít hoặc không cần thay đổi code.
Tại sao MariaDB cho Series này?
- Tương thích MySQL: Hầu hết tutorial, công cụ, và driver MySQL đều hoạt động với MariaDB.
- Mã nguồn mở thực sự: MariaDB phát hành dưới giấy phép GPL không có hạn chế thương mại từ Oracle.
- Hiệu suất: MariaDB bao gồm tối ưu hóa và storage engine không có trong MySQL.
- Phát triển tích cực: MariaDB thường có tính năng mới trước MySQL.
- Được ngành công nghiệp sử dụng: MariaDB được Wikipedia, Google, Samsung sử dụng.
Cài đặt MariaDB (Local & Docker)
Lựa chọn A: Docker (Khuyến nghị)
Docker là cách dễ nhất và sạch nhất để chạy MariaDB.
# Kéo image MariaDB chính thức và khởi động container
docker run --name blogdb \
-e MARIADB_ROOT_PASSWORD=rootpass \
-e MARIADB_DATABASE=blogdb \
-e MARIADB_USER=bloguser \
-e MARIADB_PASSWORD=blogpass \
-p 3306:3306 \
-d mariadb:11
Phân tích từng cờ:
--name blogdb— Đặt tên container “blogdb” để tham chiếu dễ dàng.-e MARIADB_ROOT_PASSWORD=rootpass— Đặt mật khẩu root (admin). Trong production, dùng mật khẩu mạnh.-e MARIADB_DATABASE=blogdb— Tự động tạo database “blogdb” khi khởi động lần đầu.-e MARIADB_USER=bloguser— Tạo user non-root “bloguser.”-e MARIADB_PASSWORD=blogpass— Đặt mật khẩu cho bloguser.-p 3306:3306— Ánh xạ cổng 3306 trên máy bạn đến cổng 3306 trong container.-d— Chạy container ở chế độ nền.mariadb:11— Sử dụng image MariaDB phiên bản 11.
Kết nối vào MariaDB:
docker exec -it blogdb mariadb -u bloguser -pblogpass blogdb
Docker Compose (tùy chọn, thuận tiện hơn):
version: '3.8'
services:
mariadb:
image: mariadb:11
container_name: blogdb
environment:
MARIADB_ROOT_PASSWORD: rootpass
MARIADB_DATABASE: blogdb
MARIADB_USER: bloguser
MARIADB_PASSWORD: blogpass
ports:
- "3306:3306"
volumes:
- mariadb_data:/var/lib/mysql
volumes:
mariadb_data:
Phần volumes đảm bảo dữ liệu tồn tại ngay cả khi container bị xóa.
Lựa chọn B: Cài đặt trực tiếp
macOS (Homebrew):
brew install mariadb
brew services start mariadb
mariadb-secure-installation
Linux (Ubuntu/Debian):
sudo apt update
sudo apt install mariadb-server
sudo systemctl start mariadb
sudo mariadb-secure-installation
Quản trị MariaDB cơ bản — User, Quyền, Database
Quản lý Database
-- Hiển thị tất cả database
SHOW DATABASES;
-- Tạo database mới
CREATE DATABASE blogdb
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
Giải thích bộ ký tự:
utf8mb4— Hỗ trợ tất cả ký tự Unicode, bao gồm emoji. Luôn dùng thay vìutf8(chỉ hỗ trợ ký tự 3-byte, không lưu được emoji).utf8mb4_unicode_ci— Collation không phân biệt hoa thường.civiết tắt của “case insensitive.”
Quản lý User
Trong production, ứng dụng không bao giờ nên kết nối bằng root. Tạo user chuyên dụng với quyền hạn chế.
-- Tạo user
CREATE USER 'bloguser'@'%' IDENTIFIED BY 'blogpass';
-- Cấp quyền trên database blogdb
GRANT ALL PRIVILEGES ON blogdb.* TO 'bloguser'@'%';
FLUSH PRIVILEGES;
'bloguser'@'%' — % có nghĩa “bất kỳ host nào” (user có thể kết nối từ mọi nơi).
Nguyên tắc Quyền tối thiểu
Một nguyên tắc bảo mật quan trọng: chỉ cấp cho mỗi user những quyền họ cần, không hơn.
- User root chỉ dùng cho quản trị (tạo database, tạo user).
- User ứng dụng (
bloguser) chỉ nên có quyền cần thiết để chạy app (SELECT, INSERT, UPDATE, DELETE). - User báo cáo chỉ đọc chỉ nên có quyền SELECT.
Ôn lại SQL thiết yếu (CREATE, INSERT, SELECT, UPDATE, DELETE, JOIN)
CREATE TABLE
CREATE TABLE authors (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
bio TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Mỗi định nghĩa cột có cấu trúc: tên_cột KIỂU_DỮ_LIỆU RÀNG_BUỘC
BIGINT— Kiểu số nguyên lớn, phù hợp cho ID.AUTO_INCREMENT— MariaDB tự động gán số tiếp theo (1, 2, 3…).PRIMARY KEY— Xác định duy nhất mỗi hàng. Không trùng lặp, không NULL.VARCHAR(100)— Chuỗi có độ dài thay đổi, tối đa 100 ký tự.NOT NULL— Cột này phải có giá trị.UNIQUE— Không hai hàng nào có cùng giá trị trong cột này.TEXT— Chuỗi không giới hạn độ dài thực tế (tối đa 65,535 ký tự).
INSERT
INSERT INTO authors (name, email, bio) VALUES
('Alice Johnson', 'alice@example.com', 'Kỹ sư phần mềm và blogger'),
('Bob Smith', 'bob@example.com', 'Lập trình viên full-stack'),
('Carol Davis', 'carol@example.com', 'Chuyên gia database');
SELECT
-- Chọn tất cả
SELECT * FROM authors;
-- Lọc với WHERE
SELECT * FROM authors WHERE name = 'Alice Johnson';
-- Tìm kiếm mẫu với LIKE
SELECT * FROM authors WHERE email LIKE '%example.com';
-- Sắp xếp
SELECT * FROM authors ORDER BY name ASC;
-- Giới hạn kết quả (phân trang)
SELECT * FROM authors ORDER BY id LIMIT 10 OFFSET 20;
-- Đếm
SELECT COUNT(*) AS total_authors FROM authors;
UPDATE
-- Cập nhật hàng cụ thể
UPDATE authors SET bio = 'Kỹ sư phần mềm cấp cao' WHERE id = 1;
-- QUAN TRỌNG: Luôn dùng WHERE với UPDATE!
-- Không có WHERE, TẤT CẢ hàng sẽ bị cập nhật!
DELETE
-- Xóa hàng cụ thể
DELETE FROM authors WHERE id = 4;
-- QUAN TRỌNG: Luôn dùng WHERE với DELETE!
JOIN — Kết hợp bảng
-- Tạo bảng thứ hai
CREATE TABLE articles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
author_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (author_id) REFERENCES authors(id)
);
-- INNER JOIN: Lấy bài viết với thông tin tác giả
SELECT
a.title AS article_title,
au.name AS author_name,
au.email AS author_email
FROM articles a
INNER JOIN authors au ON a.author_id = au.id;
-- LEFT JOIN: Lấy TẤT CẢ tác giả, kể cả những người chưa có bài viết
SELECT au.name, a.title
FROM authors au
LEFT JOIN articles a ON au.id = a.author_id;
-- GROUP BY với COUNT: Mỗi tác giả viết bao nhiêu bài?
SELECT au.name, COUNT(a.id) AS article_count
FROM authors au
LEFT JOIN articles a ON au.id = a.author_id
GROUP BY au.id, au.name
ORDER BY article_count DESC;
Tóm tắt các kiểu JOIN
| Kiểu JOIN | Trả về |
|---|---|
INNER JOIN |
Chỉ hàng khớp ở cả hai bảng |
LEFT JOIN |
Tất cả hàng từ bảng trái + khớp từ bảng phải (NULL nếu không khớp) |
RIGHT JOIN |
Tất cả hàng từ bảng phải + khớp từ bảng trái |
CROSS JOIN |
Mọi tổ hợp hàng từ cả hai bảng (ít khi dùng) |
Thực tế, INNER JOIN và LEFT JOIN đáp ứng 95% trường hợp.
Kiểu dữ liệu trong MariaDB
Kiểu số nguyên
| Kiểu | Lưu trữ | Phạm vi |
|---|---|---|
TINYINT |
1 byte | -128 đến 127 |
INT |
4 bytes | -2.1 tỷ đến 2.1 tỷ |
BIGINT |
8 bytes | -9.2 quintillion đến 9.2 quintillion |
Khuyến nghị: Dùng BIGINT cho khóa chính và khóa ngoại. Chênh lệch lưu trữ (8 byte vs 4 byte) không đáng kể.
Kiểu chuỗi
| Kiểu | Độ dài tối đa | Trường hợp sử dụng |
|---|---|---|
VARCHAR(n) |
65,535 ký tự | Chuỗi có độ dài thay đổi (tên, email, tiêu đề) |
TEXT |
65,535 ký tự | Văn bản dài (nội dung bài viết) |
MEDIUMTEXT |
16 MB | Văn bản rất dài |
Kiểu ngày giờ
| Kiểu | Trường hợp sử dụng |
|---|---|
DATE |
Ngày sinh, deadline |
DATETIME |
Timestamp sự kiện (không có timezone) |
TIMESTAMP |
Timestamp bản ghi (tự chuyển đổi UTC) |
Khuyến nghị: Dùng TIMESTAMP cho cột created_at và updated_at.
Kiểu Boolean
MariaDB không có kiểu boolean thật. BOOLEAN là bí danh cho TINYINT(1):
is_active BOOLEAN DEFAULT TRUE
-- Tương đương: is_active TINYINT(1) DEFAULT 1
-- TRUE = 1, FALSE = 0
Kiểu số thập phân
-- DECIMAL cho tiền — LUÔN dùng cho dữ liệu tài chính
price DECIMAL(10, 2) -- Tối đa: 99,999,999.99
-- KHÔNG BAO GIỜ dùng FLOAT hay DOUBLE cho tiền — chúng gây lỗi làm tròn!
Index và Khóa chính (Primary Key)
Index là gì?
Index là cấu trúc dữ liệu tăng tốc truy vấn dữ liệu. Không có index, MariaDB phải quét mọi hàng trong bảng (full table scan). Có index, nó có thể nhảy trực tiếp đến các hàng liên quan.
Phép so sánh là chỉ mục sách. Không có chỉ mục ở cuối sách, bạn phải đọc mọi trang để tìm thông tin về “Spring Boot.” Có chỉ mục, bạn tra cứu “Spring Boot,” tìm số trang, và đi thẳng đến đó.
Index kết hợp (Composite Index)
-- Nếu chúng ta thường lọc theo cả status VÀ category, index kết hợp giúp
CREATE INDEX idx_posts_status_category ON posts(status, category);
Thứ tự cột quan trọng trong index kết hợp. Index (status, category) tăng tốc truy vấn:
WHERE status = 'PUBLISHED'(dùng cột đầu)WHERE status = 'PUBLISHED' AND category = 'TUTORIAL'(dùng cả hai)
Nhưng KHÔNG tăng tốc:
WHERE category = 'TUTORIAL'(không thể bỏ qua cột đầu)
Nghĩ như danh bạ sắp theo họ, rồi tên. Bạn có thể tra “Nguyễn” (chỉ họ) hoặc “Nguyễn Văn A” (cả hai), nhưng không thể hiệu quả tra chỉ “Văn A” (chỉ tên).
Khi nào thêm Index
Thêm index trên cột thường dùng trong:
- Mệnh đề
WHERE(lọc) - Điều kiện
JOIN(khóa ngoại) - Mệnh đề
ORDER BY(sắp xếp)
Nhưng KHÔNG thêm index trên mọi cột. Index tăng tốc đọc nhưng làm chậm ghi (mỗi INSERT, UPDATE, DELETE phải cập nhật index).
Giới thiệu Thiết kế Database — Chuẩn hóa cơ bản
Chuẩn hóa là gì?
Chuẩn hóa (normalization) là quá trình tổ chức database để giảm dư thừa và cải thiện toàn vẹn dữ liệu.
Dạng chuẩn 1 (1NF) — Không có nhóm lặp
-- ❌ VI PHẠM 1NF — tags lưu dưới dạng chuỗi phân cách bằng dấu phẩy
CREATE TABLE posts_bad (
id BIGINT PRIMARY KEY,
title VARCHAR(255),
tags VARCHAR(500) -- "java,spring,tutorial" — KHÔNG phải giá trị nguyên tử!
);
-- ✅ TUÂN THEO 1NF — tags lưu trong bảng riêng, mỗi tag một hàng
CREATE TABLE posts (id BIGINT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255));
CREATE TABLE post_tags (
post_id BIGINT,
tag VARCHAR(50),
PRIMARY KEY (post_id, tag),
FOREIGN KEY (post_id) REFERENCES posts(id)
);
Dạng chuẩn 2 (2NF) và 3 (3NF)
- 2NF: Mọi cột không khóa phụ thuộc vào toàn bộ khóa chính.
- 3NF: Không có cột không khóa phụ thuộc vào cột không khóa khác.
Trong thực tế, bạn thường nhắm đến 3NF làm điểm khởi đầu. Tuy nhiên, đôi khi bạn cố tình giải chuẩn hóa (denormalize) để tăng hiệu suất — đây là đánh đổi mà chúng ta sẽ thảo luận trong Bài 16.
Sử dụng công cụ GUI (DBeaver / HeidiSQL)
DBeaver (Khuyến nghị — Đa nền tảng)
DBeaver là client database miễn phí, mã nguồn mở, hoạt động trên Windows, Mac, và Linux.
- Tải về từ dbeaver.io
- Nhấn New Database Connection → chọn MariaDB
- Điền: Host
localhost, Port3306, Databaseblogdb, Usernamebloguser, Passwordblogpass - Nhấn Test Connection → Finish
Thực hành: Thiết kế và tạo bảng cho ứng dụng Blog
Xác định thực thể và quan hệ
- Users 1:N Posts (Một người dùng viết nhiều bài)
- Posts 1:N Comments (Một bài viết có nhiều bình luận)
- Posts N:1 Categories (Nhiều bài thuộc một danh mục)
- Posts M:N Tags (Bài viết và tag nhiều-nhiều)
Tạo Schema
USE blogdb;
-- 1. BẢNG USERS
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
full_name VARCHAR(100),
bio TEXT,
role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
UNIQUE INDEX idx_users_username (username),
UNIQUE INDEX idx_users_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 2. BẢNG CATEGORIES
CREATE TABLE categories (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
slug VARCHAR(100) NOT NULL,
description TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_categories_name (name),
UNIQUE INDEX idx_categories_slug (slug)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 3. BẢNG TAGS
CREATE TABLE tags (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
slug VARCHAR(50) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_tags_name (name),
UNIQUE INDEX idx_tags_slug (slug)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 4. BẢNG POSTS
CREATE TABLE posts (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(500) NOT NULL,
slug VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
excerpt VARCHAR(1000),
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
author_id BIGINT NOT NULL,
category_id BIGINT,
published_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
UNIQUE INDEX idx_posts_slug (slug),
INDEX idx_posts_status (status),
INDEX idx_posts_author_id (author_id),
-- RESTRICT: không cho xóa user nếu có bài viết
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE RESTRICT,
-- SET NULL: nếu category bị xóa, đặt category_id thành NULL
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 5. BẢNG COMMENTS
CREATE TABLE comments (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
content TEXT NOT NULL,
post_id BIGINT NOT NULL,
author_id BIGINT NOT NULL,
parent_id BIGINT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
-- CASCADE: nếu bài viết bị xóa, tất cả bình luận cũng bị xóa
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE RESTRICT,
FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 6. BẢNG POST_TAGS (bảng nối nhiều-nhiều)
CREATE TABLE post_tags (
post_id BIGINT NOT NULL,
tag_id BIGINT NOT NULL,
PRIMARY KEY (post_id, tag_id),
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Hành động khóa ngoại
ON DELETE RESTRICT— Ngăn xóa hàng cha nếu có hàng con tồn tại.ON DELETE SET NULL— Nếu hàng cha bị xóa, đặt khóa ngoại thành NULL.ON DELETE CASCADE— Nếu hàng cha bị xóa, xóa tất cả hàng con.
Bài tập
- Thêm cột
views_countINT (mặc định 0) vào bảng posts. - Viết truy vấn “bài viết phổ biến nhất” theo số bình luận.
- Viết truy vấn “hoạt động gần đây” — 10 bình luận mới nhất với tiêu đề bài viết.
- Kiểm thử ràng buộc khóa ngoại: thử xóa user có bài viết, xóa category có bài viết.
Tổng kết
- Cơ sở dữ liệu quan hệ: Lưu trữ bền vững, dữ liệu có cấu trúc, quan hệ qua khóa ngoại.
- MariaDB: Fork mã nguồn mở từ MySQL, tương thích hoàn toàn, phát triển tích cực.
- Docker: Cách sạch nhất để chạy MariaDB — một lệnh khởi động server đã cấu hình.
- SQL thiết yếu: CREATE, INSERT, SELECT, UPDATE, DELETE, JOIN.
- Kiểu dữ liệu: BIGINT cho ID, VARCHAR cho chuỗi, TEXT cho nội dung dài, TIMESTAMP cho ngày, DECIMAL cho tiền.
- Index: Tăng tốc đọc nhưng chậm ghi. Index cột dùng trong WHERE, ORDER BY, JOIN.
- Chuẩn hóa: Tổ chức bảng giảm dư thừa (1NF, 2NF, 3NF).
- Schema blog: 6 bảng với quan hệ, ràng buộc, index, và dữ liệu mẫu.
Bài tiếp theo
Trong Bài 5, chúng ta sẽ kết nối Spring Boot với database MariaDB sử dụng Spring Data JPA và Hibernate.
Tham chiếu nhanh
| Khái niệm | Mô tả |
|---|---|
| CSDL quan hệ | Lưu dữ liệu trong bảng với hàng và cột, liên kết bằng quan hệ |
| MariaDB | Fork mã nguồn mở từ MySQL, tương thích, cộng đồng phát triển |
| SQL | Ngôn ngữ truy vấn có cấu trúc — ngôn ngữ chuẩn cho CSDL quan hệ |
| PRIMARY KEY | Xác định duy nhất mỗi hàng; tự động index |
| FOREIGN KEY | Liên kết cột đến khóa chính của bảng khác |
| INDEX | Cấu trúc dữ liệu tăng tốc truy vấn |
| JOIN | Kết hợp hàng từ nhiều bảng dựa trên cột liên quan |
| Chuẩn hóa | Tổ chức bảng giảm thiểu dư thừa (1NF, 2NF, 3NF) |
| ON DELETE CASCADE | Xóa hàng con khi hàng cha bị xóa |
| ON DELETE RESTRICT | Ngăn xóa hàng cha nếu có hàng con |
utf8mb4 |
Bộ ký tự hỗ trợ toàn bộ Unicode bao gồm emoji |
