Apache Derby Bài 10: Ràng buộc kiểm tra (CHECK) và giá trị mặc định (DEFAULT)
1. Vai trò của DEFAULT và CHECK
Trong thiết kế cơ sở dữ liệu quan hệ, ngoài việc đảm bảo tính duy nhất và liên kết thực thể (khóa chính, khóa ngoại), chúng ta cần bảo vệ tính hợp lệ của từng trường dữ liệu:
-
Mệnh đề
DEFAULT: Tự động điền giá trị chuẩn khi câu lệnhINSERTkhông truyền dữ liệu cho cột đó. Giúp giảm bớt độ dài câu lệnh SQL và tránh lỗi thiếu dữ liệu. -
Ràng buộc
CHECK: Thiết lập biểu thức điều kiện logic (Boolean expression). Mọi thao tácINSERThoặcUPDATElàm biểu thức này trả vềFALSEđều bị hệ thống chặn lại ngay lập tức.
Cả hai cơ chế này chạy trực tiếp bên trong engine của Derby, giúp giảm thiểu rủi ro dữ liệu rác lọt vào kho lưu trữ ngay cả khi tầng ứng dụng (Java, Web API) bị lỗi kiểm tra dữ liệu đầu vào (validation).
2. Giá trị mặc định (DEFAULT) trong Derby
Mệnh đề DEFAULT được khai báo ngay sau kiểu dữ liệu của cột. Giá trị mặc định có thể là:
-
Hằng số (số nguyên, chuỗi ký tự, số thực).
-
Biểu thức hoặc hằng giá trị hệ thống (
NULL,CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,USER).
SQL
CREATE TABLE tai_khoan (
id INT NOT NULL,
ten_dang_nhap VARCHAR(50) NOT NULL,
vai_tro VARCHAR(20) DEFAULT 'MEMBER',
diem_tich_luy INT DEFAULT 0,
kich_hoat SMALLINT DEFAULT 1,
thoi_gian_tao TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT pk_tai_khoan PRIMARY KEY (id)
);
Cách thức hoạt động khi chèn dữ liệu:
-
Kịch bản 1: Không khai báo cột trong danh sách INSERT
SQL
INSERT INTO tai_khoan (id, ten_dang_nhap) VALUES (1, 'dev_hieu'); -- vai_tro sẽ là 'MEMBER', diem_tich_luy = 0, kich_hoat = 1 -
Kịch bản 2: Sử dụng từ khóa
DEFAULTmột cách tường minhSQL
INSERT INTO tai_khoan (id, ten_dang_nhap, vai_tro, diem_tich_luy) VALUES (2, 'admin_root', 'ADMIN', DEFAULT); -- diem_tich_luy tự động nhận giá trị 0
3. Ràng buộc kiểm tra (CHECK) trong Derby
Ràng buộc CHECK yêu cầu một biểu thức logic đánh giá điều kiện. Trong Derby, biểu thức CHECK cần tuân thủ các quy tắc nghiêm ngặt:
-
Phải đánh giá ra kết quả
TRUE,FALSE, hoặcUNKNOWN(với giá trịNULL). Thao tác chỉ bị chặn khi kết quả làFALSE. -
Biểu thức chỉ được tham chiếu đến các cột trong cùng một bảng.
-
Không được chứa: Truy vấn con (
Subquery), hàm biến đổi theo thời gian (CURRENT_DATE,CURRENT_TIMESTAMP), hoặc các cột của bảng khác.
1. Khai báo ở cấp độ cột
Thường dùng khi điều kiện chỉ kiểm tra trên chính cột đó:
SQL
tuoi INT CONSTRAINT chk_tuoi CHECK (tuoi >= 18 AND tuoi <= 65)
2. Khai báo ở cấp độ bảng
Bắt buộc sử dụng khi điều kiện kiểm tra có sự phối hợp giữa nhiều cột trong cùng một dòng:
SQL
CONSTRAINT chk_gia_ban CHECK (gia_khuyen_mai <= gia_goc)
4. Các mẫu ràng buộc CHECK phổ biến
Mẫu 1: Kiểm tra khoảng giá trị (Range Check)
Đảm bảo điểm số, phần trăm hoặc số lượng nằm trong giới hạn cho phép:
SQL
diem_so DECIMAL(4, 2) CONSTRAINT chk_diem_hop_le CHECK (diem_so >= 0.0 AND diem_so <= 10.0)
Mẫu 2: Giới hạn danh sách giá trị cho phép (Enum Pattern)
Vì Derby không có kiểu dữ liệu ENUM nguyên bản, ta dùng CHECK (cột IN (...)) để mô phỏng:
SQL
trang_thai VARCHAR(20) CONSTRAINT chk_trang_thai_don_hang
CHECK (trang_thai IN ('CHO_XU_LY', 'DANG_GIAO', 'HOAN_THANH', 'DA_HUY'))
Mẫu 3: So sánh logic giữa nhiều cột (Cross-column Validation)
Đảm bảo ngày kết thúc luôn lớn hơn hoặc bằng ngày bắt đầu:
SQL
CONSTRAINT chk_thoi_gian_hop_le CHECK (ngay_ket_thuc >= ngay_bat_dau)
5. Thực hành: Xây dựng bảng hoàn chỉnh và kiểm thử
Mở tiện ích ij và chạy khối lệnh tạo bảng quản lý phiếu giảm giá (Voucher):
SQL
CONNECT 'jdbc:derby:firstdb';
CREATE TABLE phieu_giam_gia (
ma_voucher VARCHAR(20) NOT NULL,
ten_chuong_trinh VARCHAR(100) NOT NULL,
loai_giam_gia VARCHAR(15) DEFAULT 'TIEN_MAT',
gia_tri_giam DECIMAL(10, 2) NOT NULL,
don_hang_toi_thieu DECIMAL(12, 2) DEFAULT 0.00,
so_luot_dung INT DEFAULT 1,
ngay_bat_dau DATE NOT NULL,
ngay_het_han DATE NOT NULL,
CONSTRAINT pk_phieu_giam_gia PRIMARY KEY (ma_voucher),
-- Kiểm tra loại giảm giá chỉ được phép 1 trong 2 loại
CONSTRAINT chk_loai_giam_gia
CHECK (loai_giam_gia IN ('TIEN_MAT', 'PHAN_TRAM')),
-- Giá trị giảm và đơn hàng tối thiểu phải lớn hơn 0
CONSTRAINT chk_gia_tri_duong
CHECK (gia_tri_giam > 0 AND don_hang_toi_thieu >= 0),
-- Số lượt dùng tối thiểu là 1
CONSTRAINT chk_so_luot
CHECK (so_luot_dung > 0),
-- Ngày hết hạn phải sau hoặc bằng ngày bắt đầu
CONSTRAINT chk_han_su_dung
CHECK (ngay_het_han >= ngay_bat_dau)
);
Thử nghiệm 1: Chèn bản ghi hợp lệ sử dụng giá trị mặc định
SQL
INSERT INTO phieu_giam_gia (
ma_voucher, ten_chuong_trinh, gia_tri_giam, ngay_bat_dau, ngay_het_han
) VALUES (
'KM2026', 'Mung nam moi 2026', 50000.00, '2026-10-10', '2026-10-20'
);
Kết quả: Chèn thành công. Cột loai_giam_gia tự động nhận giá trị 'TIEN_MAT', so_luot_dung nhận giá trị 1.
Thử nghiệm 2: Cố tình vi phạm điều kiện logic của CHECK
Thử chèn phiếu có ngay_het_han nằm trước ngay_bat_dau:
SQL
INSERT INTO phieu_giam_gia (
ma_voucher, ten_chuong_trinh, gia_tri_giam, ngay_bat_dau, ngay_het_han
) VALUES (
'ERR_VOUCHER', 'Khuyen mai sai ngay', 20000.00, '2026-10-15', '2026-10-01'
);
Derby sẽ chặn câu lệnh và ném mã lỗi vi phạm kiểm tra 23513:
Plaintext
ERROR 23513: The check constraint 'CHK_HAN_SU_DUNG' was violated while performing an INSERT on table 'PHIEU_GIAM_GIA'.
6. Tổng kết
-
Mệnh đề
DEFAULThỗ trợ gán giá trị khởi tạo tĩnh hoặc các hàm thời gian/người dùng hệ thống (CURRENT_TIMESTAMP). -
Ràng buộc
CHECKđóng vai trò chốt chặn cuối cùng bảo vệ tính đúng đắn của dữ liệu, thay thế cho kiểuENUMbằng cú phápIN (...). -
Biểu thức trong
CHECKchỉ kiểm tra nội bộ trên cùng một hàng của bảng đó, không cho phép truy vấn lồng hay gọi hàm thời gian động.
Bài tiếp theo sẽ đi vào nhóm thao tác dữ liệu cốt lõi: Apache Derby Bài 11: Thao tác dữ liệu cơ bản: INSERT, UPDATE, DELETE và SELECT.
All rights reserved