Oracle Database
Cảm ơn bạn đã đăng ký khóa học!
Tài liệu về Oracle Database vẫn đang được cập nhật.
Để tôn trọng công sức của tác giả, vui lòng không chia sẻ tài liệu.
Chúc bạn học tập hiệu quả!
Cài Đặt Oracle Database và Công cụ Quản lý
1. Cài Đặt Oracle Database
Bạn có thể tải Oracle Database từ trang chính thức của Oracle:
Oracle Database có nhiều phiên bản phù hợp với từng nhu cầu:
| Phiên bản | Đặc điểm |
|---|---|
| Enterprise Edition | Dành cho doanh nghiệp lớn, hỗ trợ đầy đủ tính năng như RAC, Data Guard. |
| Standard Edition | Dành cho doanh nghiệp vừa và nhỏ, tính năng bị giới hạn so với Enterprise. |
| Express Edition | Miễn phí, nhẹ, phù hợp cho học tập và phát triển nhỏ (hạn chế tài nguyên). |
| Personal Edition | Dành cho cá nhân, đầy đủ tính năng như Enterprise nhưng chỉ dùng trên một máy. |
Lưu ý: Oracle Express Edition (XE) thường được khuyến khích cho người mới bắt đầu vì dễ cài đặt và miễn phí.
Các bước cài đặt cơ bản:
- Tải file cài đặt từ trang Oracle (ví dụ: Oracle Database 21c Express Edition).
- Giải nén và chạy file cài đặt (trên Windows là
setup.exe, trên Linux làrunInstaller). - Chọn chế độ cài đặt: Desktop Class (dành cho máy cá nhân) hoặc Server Class (dành cho hệ thống lớn).
- Cấu hình Global Database Name (tên cơ sở dữ liệu toàn cục) và SYSDBA Password (mật khẩu cho tài khoản quản trị
SYSvàSYSTEM). - Hoàn tất cài đặt và kiểm tra bằng cách kết nối.
2. Cài Đặt Công cụ Quản lý
Oracle cung cấp nhiều công cụ để quản lý cơ sở dữ liệu:
- SQL*Plus: Công cụ dòng lệnh mặc định đi kèm Oracle Database.
- Oracle SQL Developer: Công cụ GUI miễn phí, dễ sử dụng, tải tại:
- Enterprise Manager: Công cụ quản lý web dành cho quản trị viên (thường đi kèm Enterprise Edition).
Cài đặt SQL Developer:
- Tải file ZIP từ trang Oracle.
- Giải nén và chạy file
sqldeveloper.exe(không cần cài đặt). - Cấu hình kết nối đến cơ sở dữ liệu trong phần Connections.
3. Kết Nối và Làm Việc Với Oracle Database
Mở SQL*Plus hoặc SQL Developer.
Kết nối bằng tài khoản quản trị:
- Trong SQL*Plus:
CONNECT SYS/your_password AS SYSDBA; - Trong SQL Developer: Tạo kết nối mới với thông tin:
- Username:
SYShoặcSYSTEM - Password: Mật khẩu được đặt khi cài đặt
- Connection Type: Basic
- Hostname:
localhost - Port:
1521(mặc định) - SID:
xe(mặc định cho Express Edition) hoặc tên cơ sở dữ liệu bạn đặt.
- Username:
- Trong SQL*Plus:
Kiểm tra kết nối:
SELECT * FROM v$version;Kết quả sẽ hiển thị phiên bản Oracle Database đang chạy.
4. So sánh với SQL Server
Cài đặt:
- Thay SQL Server Management Studio (SSMS) bằng SQL*Plus và SQL Developer, là các công cụ phổ biến của Oracle.
- Oracle không có phiên bản "Developer" như SQL Server mà thay bằng Personal Edition hoặc Express Edition.
Kết nối:
- Oracle sử dụng SID hoặc Service Name thay vì
Server Namenhư SQL Server. - Quyền quản trị trong Oracle là
SYSDBA, khác với Windows Authentication hoặc SQL Server Authentication.
- Oracle sử dụng SID hoặc Service Name thay vì
Tạo Database StudentManagementSystem
Chúng ta có thể sử dụng câu lệnh sau để tạo Database StudentManagementSystem trong Oracle:
-- Kết nối với quyền SYSDBA để tạo user/schema (thực hiện một lần)
CONN SYS/your_password AS SYSDBA;
-- Xóa schema nếu đã tồn tại
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM dba_users WHERE username = 'STUDENTMANAGEMENTSYSTEM';
IF v_count > 0 THEN
EXECUTE IMMEDIATE 'DROP USER STUDENTMANAGEMENTSYSTEM CASCADE';
END IF;
END;
/
-- Tạo user/schema mới
CREATE USER STUDENTMANAGEMENTSYSTEM IDENTIFIED BY sms_password
DEFAULT TABLESPACE USERS
QUOTA UNLIMITED ON USERS;
-- Gán quyền cơ bản
GRANT CONNECT, RESOURCE TO STUDENTMANAGEMENTSYSTEM;
-- Kết nối với schema STUDENTMANAGEMENTSYSTEM
CONN STUDENTMANAGEMENTSYSTEM/sms_password;
-- Xóa các bảng nếu tồn tại
BEGIN
FOR t IN (SELECT TABLE_NAME FROM USER_TABLES) LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || t.TABLE_NAME || ' PURGE';
END LOOP;
END;
/
-- Tạo bảng Students
CREATE TABLE Students (
StudentID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Name NVARCHAR2(50) NOT NULL,
Birthdate DATE
);
-- Tạo bảng Courses
CREATE TABLE Courses (
CourseID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Name NVARCHAR2(50) NOT NULL,
Description NVARCHAR2(255) NOT NULL
);
-- Tạo bảng Enrollments
CREATE TABLE Enrollments (
EnrollmentID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
StudentID NUMBER NOT NULL,
CourseID NUMBER NOT NULL,
EnrollmentDate DATE DEFAULT SYSDATE NOT NULL,
CONSTRAINT fk_enrollments_students FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
CONSTRAINT fk_enrollments_courses FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);
-- Chèn dữ liệu vào bảng Courses
INSERT INTO Courses (Name, Description)
VALUES (N'Toán học', N'Khóa học về toán học cơ bản và nâng cao'),
(N'Lập trình', N'Khóa học lập trình và thuật toán'),
(N'Tiếng Nhật', N'Khóa học tiếng Nhật từ sơ cấp đến trung cấp'),
(N'Tiếng Anh', N'Khóa học tiếng Anh giao tiếp');
-- Chèn dữ liệu vào bảng Students
INSERT INTO Students (Name, Birthdate)
VALUES (N'Trần Nhật Anh', TO_DATE('2002-01-01', 'YYYY-MM-DD')),
(N'Phạm Thanh Hương', TO_DATE('2000-05-15', 'YYYY-MM-DD')),
(N'Đặng Hoàng Nam', TO_DATE('2003-03-07', 'YYYY-MM-DD')),
(N'Vũ Khánh Linh', TO_DATE('2005-11-25', 'YYYY-MM-DD'));
-- Chèn dữ liệu vào bảng Enrollments (không chỉ định EnrollmentDate, dùng mặc định SYSDATE)
INSERT INTO Enrollments (StudentID, CourseID)
VALUES (1, 1),
(1, 2);
-- Chèn dữ liệu vào bảng Enrollments với EnrollmentDate cụ thể
INSERT INTO Enrollments (StudentID, CourseID, EnrollmentDate)
VALUES (2, 3, TO_DATE('2025-01-01', 'YYYY-MM-DD')),
(3, 4, TO_DATE('2025-02-28', 'YYYY-MM-DD'));
-- Commit dữ liệu
COMMIT;
So sánh với SQL Server
Tạo Database:
- SQL Server dùng
CREATE DATABASEvàUSE. Oracle không có khái niệm database độc lập như vậy; thay vào đó, bạn tạo một schema (user) bằngCREATE USER. - Xóa database trong Oracle là xóa user với
DROP USER ... CASCADE. - Quyền truy cập được gán qua
GRANT CONNECT, RESOURCE.
- SQL Server dùng
Xóa bảng:
- SQL Server dùng
ALTER DATABASE ... SET SINGLE_USERđể đóng kết nối trước khi xóa. Oracle không cần bước này; bạn chỉ cần xóa các bảng trong schema bằng vòng lặp PL/SQL vớiDROP TABLE ... PURGE.
- SQL Server dùng
Tạo bảng:
- Kiểu dữ liệu:
INTthay bằngNUMBER.NVARCHARthay bằngNVARCHAR2(hỗ trợ Unicode).
- IDENTITY: Oracle dùng
GENERATED ALWAYS AS IDENTITYthayIDENTITY(1,1). - DEFAULT:
GETDATE()trong SQL Server thay bằngSYSDATEtrong Oracle. - Khóa ngoại: Oracle yêu cầu đặt tên rõ ràng (
CONSTRAINT fk_name) thay vì để mặc định như SQL Server.
- Kiểu dữ liệu:
Chèn dữ liệu:
- Ngày tháng: SQL Server chấp nhận định dạng ngày trực tiếp (như
'2002-01-01'). Oracle yêu cầuTO_DATEđể chuyển chuỗi thành kiểuDATE. - Transaction: SQL Server không cần
COMMITrõ ràng trong script này vì mặc định autocommit trong một số trường hợp. Oracle yêu cầuCOMMITđể lưu dữ liệu vĩnh viễn. - Cú pháp
INSERT INTO ... VALUESgiữ nguyên.
- Ngày tháng: SQL Server chấp nhận định dạng ngày trực tiếp (như
Phân tách lệnh:
- SQL Server dùng
GOđể phân tách các batch. Oracle không cầnGO, nhưng dùng/để kết thúc khối PL/SQL hoặc chạy script trong SQL*Plus/SQL Developer.
- SQL Server dùng
Quản lý Cơ sở dữ liệu (Database)
Trong Oracle, một Database là một tập hợp các tệp dữ liệu vật lý (data files, control files, redo log files). Một Instance là tập hợp các tiến trình và bộ nhớ để quản lý database. Oracle thường sử dụng khái niệm Schema để quản lý các đối tượng (bảng, view,...) thuộc về một người dùng.
Tạo cơ sở dữ liệu (CREATE DATABASE)
Oracle không khuyến khích tạo database mới bằng lệnh SQL thủ công vì quá trình này phức tạp. Thay vào đó, bạn sử dụng công cụ Database Configuration Assistant (DBCA):
- Chạy DBCA từ menu Start (Windows) hoặc lệnh
dbca(Linux). - Chọn Create Database > Typical Configuration.
- Đặt tên database (ví dụ:
StudentManagementSystemTest) và mật khẩu quản trị. - Hoàn tất để tạo database.
Tuy nhiên, nếu muốn tạo thủ công (dành cho quản trị viên nâng cao):
CREATE DATABASE StudentManagementSystemTest
DATAFILE '/u01/app/oracle/oradata/StudentManagementSystemTest/system01.dbf' SIZE 100M
SYSAUX DATAFILE '/u01/app/oracle/oradata/StudentManagementSystemTest/sysaux01.dbf' SIZE 100M
LOGFILE GROUP 1 ('/u01/app/oracle/oradata/StudentManagementSystemTest/redo01.log') SIZE 50M,
GROUP 2 ('/u01/app/oracle/oradata/StudentManagementSystemTest/redo02.log') SIZE 50M
CHARACTER SET AL32UTF8
NATIONAL CHARACTER SET AL16UTF16;
Xóa cơ sở dữ liệu (DROP DATABASE)
Xóa database bằng DBCA hoặc lệnh SQL:
DROP DATABASE;
Lưu ý: Phải kết nối với quyền SYSDBA và database phải ở trạng thái MOUNT.
Sao lưu cơ sở dữ liệu
Oracle sử dụng Recovery Manager (RMAN) để sao lưu:
RMAN> BACKUP DATABASE;
Lưu bản sao lưu vào thư mục mặc định hoặc chỉ định:
RMAN> BACKUP DATABASE TO DESTINATION '/u01/backup';
Khôi phục cơ sở dữ liệu
Khôi phục bằng RMAN:
RMAN> RESTORE DATABASE;
RMAN> RECOVER DATABASE;
Lưu ý: Cần cấu hình trước các tệp sao lưu và đảm bảo instance đang chạy.
Chọn schema để làm việc
Trong Oracle, bạn làm việc trong một schema (thường gắn với một user):
ALTER SESSION SET CURRENT_SCHEMA = StudentManagementSystem;
Hoặc kết nối trực tiếp với user sở hữu schema:
CONNECT StudentManagementSystem/password;
Liệt kê cơ sở dữ liệu
Oracle không có lệnh trực tiếp liệt kê tất cả database như SQL Server. Thay vào đó, bạn kiểm tra các instance hoặc schema:
- Liệt kê các schema (người dùng):
SELECT USERNAME FROM ALL_USERS;
- Kiểm tra database hiện tại:
SELECT NAME FROM v$database;
Quản lý bảng (Table)
Trong Oracle, bảng được tạo trong một schema (thường gắn với một user). Mỗi bảng thuộc về schema của user tạo ra nó, và bạn cần quyền cụ thể để truy cập bảng từ schema khác.
Tạo bảng (CREATE TABLE)
CREATE TABLE Teachers (
TeacherID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Name VARCHAR2(50) NOT NULL
);
NUMBER GENERATED ALWAYS AS IDENTITY: Tạo cột tự tăng giốngIDENTITYtrong SQL Server.VARCHAR2: Kiểu chuỗi phổ biến trong Oracle, khác vớiVARCHARcủa SQL Server.
Xóa bảng (DROP TABLE)
DROP TABLE Teachers;
- Để xóa bảng và các ràng buộc liên quan:
DROP TABLE Teachers PURGE;
Lưu ý: PURGE xóa vĩnh viễn, không thể khôi phục từ Recycle Bin của Oracle.
Thay đổi cấu trúc bảng (ALTER TABLE)
Thêm cột mới
ALTER TABLE Students ADD (Email VARCHAR2(100));
Thay đổi kiểu dữ liệu của cột
ALTER TABLE Students MODIFY (Email VARCHAR2(150));
Lưu ý: Oracle không cho phép thay đổi kiểu dữ liệu nếu cột đã chứa dữ liệu không tương thích.
Đổi tên cột
ALTER TABLE Students RENAME COLUMN Email TO StudentEmail;
- Oracle không dùng
sp_renamenhư SQL Server mà dùng cú phápRENAME COLUMN.
Xóa cột
ALTER TABLE Students DROP COLUMN StudentEmail;
Đổi tên bảng
ALTER TABLE Students RENAME TO Pupils;
Liệt kê bảng
Liệt kê các bảng trong schema hiện tại:
SELECT TABLE_NAME
FROM USER_TABLES;
- Nếu muốn liệt kê bảng từ tất cả schema mà user có quyền truy cập:
SELECT TABLE_NAME
FROM ALL_TABLES;
Kiểu dữ liệu (Data Types)
Oracle có hệ thống kiểu dữ liệu riêng, khác với SQL Server ở một số điểm.
Kiểu số (Numeric Data Types)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
NUMBER | Kiểu số linh hoạt, có thể lưu số nguyên hoặc thập phân (tối đa 38 chữ số). | 10, 12345.67 |
NUMBER(p,s) | Số thập phân với độ chính xác p (tổng chữ số) và s (số chữ số sau dấu chấm). | NUMBER(10,2) → 12345.67 |
INTEGER | Số nguyên, tương đương NUMBER(38) không thập phân. | 100, -50 |
FLOAT | Số thực dấu phẩy động, độ chính xác cao, có thể có lỗi làm tròn. | 3.14159 |
BINARY_FLOAT | Số thực 32-bit, nhanh hơn FLOAT nhưng ít chính xác hơn. | 3.14 |
BINARY_DOUBLE | Số thực 64-bit, tương tự BINARY_FLOAT nhưng chính xác hơn. | 3.14159 |
Lưu ý: Oracle không có kiểu TINYINT, SMALLINT, hay BIGINT riêng biệt như SQL Server; tất cả đều dùng NUMBER với độ chính xác phù hợp.
Kiểu chuỗi (String Data Types)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
CHAR(n) | Chuỗi cố định độ dài n ký tự (mặc định byte), luôn chiếm n byte/ký tự. | 'ABCD ' (5 ký tự) |
VARCHAR2(n) | Chuỗi độ dài thay đổi, tối đa n byte (hoặc ký tự nếu khai báo CHAR). | 'John Doe' |
NCHAR(n) | Chuỗi Unicode cố định độ dài, hỗ trợ đa ngôn ngữ, mỗi ký tự 2 byte. | N'Hoàng Hiệp' |
NVARCHAR2(n) | Chuỗi Unicode độ dài thay đổi, tối đa n ký tự. | N'Hoàng Hiệp' |
CLOB | Lưu chuỗi dài (Character Large Object), tối đa 4GB. | 'Lorem ipsum...' |
✅ Dùng CHAR(n), NCHAR(n) khi:
- Chuỗi có độ dài cố định (mã số, mã bưu chính).
- Hiệu suất cao hơn khi không cần kiểm tra độ dài.
✅ Dùng VARCHAR2(n), NVARCHAR2(n) khi:
- Chuỗi độ dài thay đổi (tên, mô tả).
- Tiết kiệm bộ nhớ.
✅ Dùng CLOB khi:
- Chuỗi rất dài (bài viết, JSON/XML).
Lưu ý: Oracle không khuyến khích dùng VARCHAR; luôn dùng VARCHAR2.
Kiểu ngày và thời gian (Date & Time Data Types)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
DATE | Lưu ngày và giờ, chính xác đến giây, từ 4712 BC đến 9999 AD. | '2025-03-01 12:30:45' |
TIMESTAMP | Lưu ngày giờ với độ chính xác đến phần nhỏ của giây (mặc định 6 chữ số). | '2025-03-01 12:30:45.123456' |
TIMESTAMP WITH TIME ZONE | Như TIMESTAMP, nhưng kèm múi giờ. | '2025-03-01 12:30:45.123456 +07:00' |
INTERVAL YEAR TO MONTH | Lưu khoảng thời gian tính bằng năm và tháng. | '2-6' (2 năm 6 tháng) |
INTERVAL DAY TO SECOND | Lưu khoảng thời gian tính bằng ngày, giờ, phút, giây. | '3 12:30:45.123' |
Lưu ý: Oracle không có kiểu TIME riêng như SQL Server; dùng TIMESTAMP hoặc DATE.
Kiểu dữ liệu nhị phân và định danh (Binary & Unique Identifier)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
RAW(n) | Dữ liệu nhị phân độ dài tối đa n byte (tối đa 2000 byte). | 0x1A2B3C |
BLOB | Lưu dữ liệu nhị phân lớn (Binary Large Object), tối đa 4GB. | Ảnh, file PDF |
Lưu ý: Oracle không có kiểu UNIQUEIDENTIFIER như SQL Server; dùng RAW(16) hoặc chuỗi GUID với VARCHAR2(36).
Ví dụ tạo bảng với kiểu dữ liệu phổ biến
CREATE TABLE Employees (
EmployeeID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Name NVARCHAR2(100) NOT NULL,
Birthdate DATE,
WorkStartTime TIMESTAMP,
Salary NUMBER(10,2),
ProfilePicture BLOB,
IsActive NUMBER(1) DEFAULT 1 -- 1: True, 0: False
);
Ví dụ tạo bảng với tất cả kiểu dữ liệu
CREATE TABLE AllDataTypesExample (
ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
IntNumber NUMBER(10) NOT NULL,
DecimalNumber NUMBER(10,2),
FloatNumber FLOAT,
FixedText CHAR(10),
VariableText VARCHAR2(255),
UnicodeText NVARCHAR2(255),
LongText CLOB,
BirthDate DATE,
FullTimestamp TIMESTAMP,
ProfilePicture BLOB,
CreatedAt DATE DEFAULT SYSDATE
);
Biến (Variable)
Trong Oracle, biến được sử dụng trong các khối PL/SQL (ngôn ngữ lập trình của Oracle) để lưu trữ dữ liệu tạm thời. Biến thường xuất hiện trong anonymous blocks, stored procedures, hoặc functions.
Khai báo biến (DECLARE)
Biến được khai báo trong khối DECLARE của PL/SQL:
DECLARE
v_StudentName VARCHAR2(50);
v_BirthYear NUMBER(4);
BEGIN
NULL; -- Thân khối PL/SQL
END;
/
/là ký hiệu kết thúc khối PL/SQL trong SQL*Plus hoặc SQL Developer.
Gán giá trị cho biến
Oracle hỗ trợ gán giá trị bằng toán tử := hoặc lệnh SELECT ... INTO.
Gán giá trị bằng :=
DECLARE
v_StudentName VARCHAR2(50);
BEGIN
v_StudentName := 'Hoàng Hiệp';
DBMS_OUTPUT.PUT_LINE(v_StudentName);
END;
/
DBMS_OUTPUT.PUT_LINE: Hàm in giá trị ra màn hình (tương tựPRINTtrong SQL Server).
Gán giá trị bằng SELECT ... INTO
DECLARE
v_BirthYear NUMBER(4);
BEGIN
SELECT EXTRACT(YEAR FROM Birthdate) INTO v_BirthYear
FROM Students
WHERE StudentID = 1;
DBMS_OUTPUT.PUT_LINE('Năm sinh: ' || v_BirthYear);
END;
/
EXTRACT(YEAR FROM ...): Lấy năm từ ngày, thay choYEAR()trong SQL Server.||: Toán tử nối chuỗi trong Oracle.
Sử dụng biến
Biến có thể dùng trong các câu lệnh SQL trong khối PL/SQL:
DECLARE
v_Year NUMBER(4) := 2000;
BEGIN
FOR rec IN (SELECT * FROM Students WHERE EXTRACT(YEAR FROM Birthdate) = v_Year) LOOP
DBMS_OUTPUT.PUT_LINE(rec.Name);
END LOOP;
END;
/
Biến bảng (Collection)
Oracle không có "biến bảng" như SQL Server mà dùng collections (ARRAY-like structures) như VARRAY hoặc TABLE OF.
Khai báo collection
DECLARE
TYPE StudentTab IS TABLE OF Students%ROWTYPE;
v_StudentTable StudentTab;
BEGIN
SELECT * BULK COLLECT INTO v_StudentTable
FROM Students
WHERE ROWNUM <= 5; -- Giới hạn 5 dòng
FOR i IN 1..v_StudentTable.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(v_StudentTable(i).Name);
END LOOP;
END;
/
%ROWTYPE: Kiểu dữ liệu dựa trên cấu trúc bảng.BULK COLLECT: Thu thập nhiều dòng vào collection.
Biến toàn cục (System Variables)
Oracle có các biến hệ thống bắt đầu bằng : hoặc thông qua gói SYS:
SQL%ROWCOUNT: Số dòng bị ảnh hưởng bởi câu lệnh trước đó.USER: Tên schema hiện tại.
BEGIN
UPDATE Students SET Name = 'New Name' WHERE StudentID = 1;
DBMS_OUTPUT.PUT_LINE('Số dòng cập nhật: ' || SQL%ROWCOUNT);
DBMS_OUTPUT.PUT_LINE('Người dùng: ' || USER);
END;
/
Hàm (Functions)
Oracle hỗ trợ các hàm tích hợp và cho phép tạo hàm người dùng trong PL/SQL.
Hàm xử lý chuỗi (String Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
LENGTH(string) | Trả về độ dài chuỗi | LENGTH('Hello') → 5 |
SUBSTR(string, start, length) | Cắt chuỗi con | SUBSTR('SQL Server', 5, 6) → 'Server' |
INSTR(string, substring) | Tìm vị trí chuỗi con | INSTR('Learn SQL', 'SQL') → 7 |
UPPER(string) | Chuyển thành chữ in hoa | UPPER('sql') → 'SQL' |
LOWER(string) | Chuyển thành chữ thường | LOWER('SQL') → 'sql' |
TRIM(string) | Xóa khoảng trắng 2 bên chuỗi | TRIM(' SQL ') → 'SQL' |
REPLACE(string, old, new) | Thay thế chuỗi con | REPLACE('Hello World', 'World', 'SQL') → 'Hello SQL' |
CONCAT(string1, string2) | Nối chuỗi | CONCAT('Hello', ' SQL') → 'Hello SQL' |
Hàm xử lý số (Numeric Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
ABS(number) | Trị tuyệt đối | ABS(-5) → 5 |
CEIL(number) | Làm tròn lên | CEIL(4.2) → 5 |
FLOOR(number) | Làm tròn xuống | FLOOR(4.9) → 4 |
ROUND(number, decimals) | Làm tròn số | ROUND(4.567, 2) → 4.57 |
POWER(base, exponent) | Lũy thừa | POWER(2, 3) → 8 |
SQRT(number) | Căn bậc hai | SQRT(9) → 3 |
MOD(number, divisor) | Chia lấy dư | MOD(10, 3) → 1 |
Hàm xử lý ngày tháng (Date Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
SYSDATE | Trả về ngày giờ hiện tại | SYSDATE → '2025-03-15 10:15:30' |
SYSTIMESTAMP | Ngày giờ với múi giờ | SYSTIMESTAMP → '2025-03-15 10:15:30.123456 +07:00' |
EXTRACT(part FROM date) | Lấy phần từ ngày | EXTRACT(YEAR FROM DATE '2025-03-15') → 2025 |
ADD_MONTHS(date, months) | Cộng tháng | ADD_MONTHS('2025-03-15', 10) → '2026-01-15' |
MONTHS_BETWEEN(date1, date2) | Khoảng cách giữa hai ngày (tháng) | MONTHS_BETWEEN('2025-03-15', '2025-01-01') → 2.45 |
TO_CHAR(date, format) | Định dạng ngày | TO_CHAR(SYSDATE, 'DD/MM/YYYY') → '15/03/2025' |
LAST_DAY(date) | Ngày cuối tháng | LAST_DAY('2025-03-15') → '2025-03-31' |
Hàm nâng cao (Advanced Functions)
Hàm chuyển đổi dữ liệu
| Hàm | Chức năng | Ví dụ |
|---|---|---|
TO_CHAR(expression, format) | Chuyển sang chuỗi | TO_CHAR(123, '999') → '123' |
TO_NUMBER(string) | Chuyển chuỗi thành số | TO_NUMBER('123.45') → 123.45 |
TO_DATE(string, format) | Chuyển chuỗi thành ngày | TO_DATE('15/03/2025', 'DD/MM/YYYY') → '2025-03-15' |
Hàm xử lý giá trị NULL
| Hàm | Chức năng | Ví dụ |
|---|---|---|
NVL(expr1, expr2) | Thay thế NULL bằng giá trị khác | NVL(NULL, 'N/A') → 'N/A' |
NULLIF(val1, val2) | Trả về NULL nếu hai giá trị bằng nhau | NULLIF(10, 10) → NULL |
COALESCE(val1, val2, …) | Giá trị không NULL đầu tiên | COALESCE(NULL, 'SQL', 'Oracle') → 'SQL' |
Ràng buộc dữ liệu
Ràng buộc đảm bảo tính toàn vẹn dữ liệu trong Oracle.
Tổng quan các ràng buộc
| Ràng buộc | Chức năng |
|---|---|
NOT NULL | Không cho phép giá trị NULL. |
UNIQUE | Đảm bảo giá trị trong cột là duy nhất. |
PRIMARY KEY | Kết hợp NOT NULL và UNIQUE, định danh duy nhất mỗi dòng. |
FOREIGN KEY | Tạo mối quan hệ giữa các bảng, đảm bảo dữ liệu hợp lệ. |
CHECK | Kiểm tra giá trị nhập vào thỏa mãn điều kiện cụ thể. |
NOT NULL
CREATE TABLE Teachers (
TeacherID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Name VARCHAR2(50) NOT NULL
);
UNIQUE
CREATE TABLE Doctors (
DoctorID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Email VARCHAR2(100) UNIQUE
);
PRIMARY KEY
CREATE TABLE Departments (
DeptID NUMBER PRIMARY KEY,
DeptName VARCHAR2(50) NOT NULL
);
FOREIGN KEY
CREATE TABLE Enrollments (
StudentID NUMBER,
CourseID NUMBER,
FOREIGN KEY (StudentID) REFERENCES Students(StudentID)
ON DELETE CASCADE,
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
ON DELETE SET NULL
);
ON DELETE CASCADE: Xóa bản ghi cha sẽ xóa các bản ghi con.ON DELETE SET NULL: Xóa bản ghi cha sẽ đặt khóa ngoại thànhNULL.
CHECK
CREATE TABLE Employees (
EmployeeID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Age NUMBER CHECK (Age >= 18)
);
Ví dụ tạo bảng có đầy đủ ràng buộc
CREATE TABLE Departments (
DeptID NUMBER PRIMARY KEY,
DeptName VARCHAR2(100) NOT NULL UNIQUE
);
CREATE TABLE Employees (
EmployeeID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
Name VARCHAR2(100) NOT NULL,
Email VARCHAR2(100) UNIQUE,
Birthdate DATE CHECK (Birthdate >= '1990-01-01'),
DepartmentID NUMBER,
FOREIGN KEY (DepartmentID) REFERENCES Departments(DeptID)
);
Thao tác dữ liệu
Thêm dữ liệu (INSERT INTO)
Dùng để thêm dữ liệu vào bảng.
Thêm một sinh viên mới:
INSERT INTO Students (Name, Birthdate)
VALUES ('Hoàng Hiệp', TO_DATE('2001-05-20', 'YYYY-MM-DD'));
TO_DATE: Chuyển chuỗi thành kiểuDATEtheo định dạng.
Cập nhật dữ liệu (UPDATE)
Dùng để cập nhật dữ liệu trong bảng.
Cập nhật ngày sinh cho sinh viên có StudentID = 4:
UPDATE Students
SET Birthdate = TO_DATE('2000-06-15', 'YYYY-MM-DD')
WHERE StudentID = 4;
Xóa dữ liệu (DELETE)
Dùng để xóa dữ liệu trong bảng.
Xóa sinh viên có StudentID = 2:
DELETE FROM Students
WHERE StudentID = 2;
Lưu ý:
- Nếu không có
WHERE, toàn bộ dữ liệu trong bảng sẽ bị xóa. - Dữ liệu xóa sẽ vào Recycle Bin (trừ khi dùng
PURGE), có thể khôi phục bằng:
FLASHBACK TABLE Students TO BEFORE DROP;
Xóa toàn bộ dữ liệu (TRUNCATE TABLE)
Dùng để xóa toàn bộ dữ liệu trong bảng, nhanh hơn DELETE:
TRUNCATE TABLE Students;
Lưu ý:
- Không ghi log từng dòng, không khôi phục được.
- Reset giá trị
IDENTITY(sequence nếu dùng).
Sao chép dữ liệu vào bảng mới (CREATE TABLE AS SELECT)
Tương đương SELECT INTO trong SQL Server:
CREATE TABLE StudentsBackup
AS
SELECT * FROM Students;
Chèn dữ liệu từ bảng khác (INSERT INTO ... SELECT)
INSERT INTO StudentsBackup (Name, Birthdate)
SELECT Name, Birthdate
FROM Students;
Hợp nhất dữ liệu (MERGE)
Dùng để thực hiện INSERT, UPDATE, hoặc DELETE dựa trên điều kiện:
MERGE INTO StudentsBackup tgt
USING Students src
ON (tgt.StudentID = src.StudentID)
WHEN MATCHED THEN
UPDATE SET tgt.Name = src.Name, tgt.Birthdate = src.Birthdate
WHEN NOT MATCHED THEN
INSERT (StudentID, Name, Birthdate)
VALUES (src.StudentID, src.Name, src.Birthdate);
- Oracle không có
WHEN NOT MATCHED BY SOURCE THEN DELETEnhư SQL Server; cần xử lý riêng nếu muốn xóa.
Kiểm tra tồn tại (EXISTS)
Kiểm tra dữ liệu trước khi thao tác:
BEGIN
IF EXISTS (SELECT 1 FROM Students WHERE Name = 'Hoàng Hiệp') THEN
DBMS_OUTPUT.PUT_LINE('Sinh viên đã tồn tại');
ELSE
INSERT INTO Students (Name, Birthdate)
VALUES ('Hoàng Hiệp', TO_DATE('2001-05-20', 'YYYY-MM-DD'));
END IF;
END;
/
Lấy dữ liệu bị ảnh hưởng (RETURNING)
Tương đương OUTPUT trong SQL Server:
DECLARE
v_StudentID NUMBER;
v_Name VARCHAR2(50);
BEGIN
DELETE FROM Students
WHERE Birthdate < TO_DATE('2000-01-01', 'YYYY-MM-DD')
RETURNING StudentID, Name INTO v_StudentID, v_Name;
DBMS_OUTPUT.PUT_LINE('Xóa: ' || v_StudentID || ' - ' || v_Name);
END;
/
RETURNINGtrả về dữ liệu bị ảnh hưởng vào biến trong PL/SQL.
Toán tử và điều kiện lọc
Toán tử so sánh
Các toán tử phổ biến trong Oracle:
| Toán tử | Chức năng |
|---|---|
= | So sánh bằng |
!= hoặc <> | So sánh khác |
> | Lớn hơn |
< | Nhỏ hơn |
>= | Lớn hơn hoặc bằng |
<= | Nhỏ hơn hoặc bằng |
BETWEEN | Trong khoảng |
IN | Trong danh sách |
LIKE | Khớp mẫu ký tự |
AND | Và |
OR | Hoặc |
NOT | Phủ định |
AND
Lấy sinh viên có tên bắt đầu bằng 'P' và sinh sau 01/01/2000:
SELECT * FROM Students
WHERE Name LIKE 'P%' AND Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD');
OR
Lấy sinh viên có tên là “Trần Nhật Anh” hoặc “Vũ Khánh Linh”:
SELECT * FROM Students
WHERE Name = 'Trần Nhật Anh' OR Name = 'Vũ Khánh Linh';
NOT
Lấy tất cả sinh viên trừ “Trần Nhật Anh”:
SELECT * FROM Students
WHERE NOT Name = 'Trần Nhật Anh';
LIKE
Tìm sinh viên có tên bắt đầu bằng “T”:
SELECT * FROM Students
WHERE Name LIKE 'T%';
Wildcards (Ký tự đại diện)
%: Đại diện cho 0 hoặc nhiều ký tự._: Đại diện cho một ký tự. Tìm sinh viên có tên chứa “ng”:
SELECT * FROM Students
WHERE Name LIKE '%ng%';
IN
Lấy sinh viên có tên trong danh sách:
SELECT * FROM Students
WHERE Name IN ('Vũ Khánh Linh', 'Trần Nhật Anh', 'Lê Thu Hà');
BETWEEN
Lấy sinh viên sinh từ 2000 đến 2003:
SELECT * FROM Students
WHERE Birthdate BETWEEN TO_DATE('2000-01-01', 'YYYY-MM-DD')
AND TO_DATE('2003-12-31', 'YYYY-MM-DD');
IS NULL và IS NOT NULL
Lấy sinh viên chưa có ngày sinh:
SELECT * FROM Students
WHERE Birthdate IS NULL;
ANY và ALL
ANY: So sánh với bất kỳ giá trị nào trong tập con.ALL: So sánh với tất cả giá trị trong tập con. Lấy sinh viên sinh muộn hơn ít nhất một người trong danh sách:
SELECT * FROM Students
WHERE Birthdate > ANY (
SELECT Birthdate
FROM Students
WHERE Name IN ('Vũ Khánh Linh', 'Trần Nhật Anh')
);
EXISTS
Lấy sinh viên đã đăng ký khóa học:
SELECT Name
FROM Students s
WHERE EXISTS (
SELECT 1
FROM Enrollments e
WHERE e.StudentID = s.StudentID
);
IF ELSE
Dùng trong PL/SQL:
DECLARE
v_TotalStudents NUMBER;
BEGIN
SELECT COUNT(*) INTO v_TotalStudents FROM Students;
IF v_TotalStudents > 100 THEN
DBMS_OUTPUT.PUT_LINE('Có hơn 100 sinh viên.');
ELSE
DBMS_OUTPUT.PUT_LINE('Số lượng sinh viên <= 100.');
END IF;
END;
/
CASE WHEN
Dùng trong SQL hoặc PL/SQL:
SELECT Name,
CASE
WHEN EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM Birthdate) < 18 THEN 'Teen'
WHEN EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM Birthdate) BETWEEN 18 AND 24 THEN 'Young Adult'
ELSE 'Adult'
END AS AgeGroup
FROM Students;
Truy vấn dữ liệu
Lệnh SELECT cơ bản
Lấy tất cả dữ liệu từ bảng Students:
SELECT * FROM Students;
Lấy cột cụ thể
Lấy Name và Birthdate từ bảng Students:
SELECT Name, Birthdate FROM Students;
Đặt bí danh (Alias)
Sử dụng AS để đặt tên khác cho cột hoặc bảng:
SELECT Name AS StudentName, Birthdate AS DOB FROM Students;
- Oracle cho phép bỏ
ASkhi đặt alias cho cột:
SELECT Name StudentName, Birthdate DOB FROM Students;
Sắp xếp dữ liệu (ORDER BY)
Sắp xếp sinh viên theo tên:
SELECT Name, Birthdate
FROM Students
ORDER BY Name ASC;
ASC: Tăng dần (mặc định).DESC: Giảm dần.
Sắp xếp theo nhiều cột:
SELECT Name, Birthdate
FROM Students
ORDER BY Birthdate DESC, Name ASC;
Lấy một số dòng nhất định (FETCH)
Oracle dùng FETCH thay cho TOP trong SQL Server (từ Oracle 12c trở lên):
SELECT Name, Birthdate
FROM Students
ORDER BY Name
FETCH FIRST 5 ROWS ONLY;
- Lấy 5 dòng đầu tiên.
FETCH FIRST 5 ROWS WITH TIES: Lấy thêm nếu có giá trị trùng lặp ở dòng thứ 5.
Trước Oracle 12c, dùng WHERE ROWNUM:
SELECT Name, Birthdate
FROM Students
WHERE ROWNUM <= 5
ORDER BY Name;
Lưu ý: ROWNUM yêu cầu sắp xếp sau khi lọc, nên cần subquery nếu muốn chính xác:
SELECT Name, Birthdate
FROM (SELECT Name, Birthdate FROM Students ORDER BY Name)
WHERE ROWNUM <= 5;
Giới hạn dòng với OFFSET
Lấy dòng 6 đến 10:
SELECT Name, Birthdate
FROM Students
ORDER BY Name
OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;
Lọc dữ liệu với WHERE
Lấy sinh viên sinh sau năm 2000:
SELECT Name, Birthdate
FROM Students
WHERE Birthdate > TO_DATE('2000-12-31', 'YYYY-MM-DD');
Hàm tổng hợp (Aggregate Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
COUNT(*) | Đếm số dòng | SELECT COUNT(*) FROM Students; |
SUM(column) | Tổng giá trị cột | SELECT SUM(Salary) FROM Employees; |
AVG(column) | Trung bình cộng | SELECT AVG(Salary) FROM Employees; |
MAX(column) | Giá trị lớn nhất | SELECT MAX(Birthdate) FROM Students; |
MIN(column) | Giá trị nhỏ nhất | SELECT MIN(Birthdate) FROM Students; |
Ví dụ: Đếm số sinh viên sinh năm 2001:
SELECT COUNT(*) AS Total
FROM Students
WHERE EXTRACT(YEAR FROM Birthdate) = 2001;
Nhóm dữ liệu (GROUP BY)
Đếm số sinh viên theo năm sinh:
SELECT EXTRACT(YEAR FROM Birthdate) AS BirthYear, COUNT(*) AS Total
FROM Students
GROUP BY EXTRACT(YEAR FROM Birthdate);
Lọc nhóm (HAVING)
Lấy các năm có hơn 10 sinh viên:
SELECT EXTRACT(YEAR FROM Birthdate) AS BirthYear, COUNT(*) AS Total
FROM Students
GROUP BY EXTRACT(YEAR FROM Birthdate)
HAVING COUNT(*) > 10;
Truy vấn con (Subquery)
Lấy sinh viên có ngày sinh nhỏ nhất:
SELECT Name, Birthdate
FROM Students
WHERE Birthdate = (SELECT MIN(Birthdate) FROM Students);
WITH Clause (Common Table Expression - CTE)
Tương đương CTE trong SQL Server:
WITH YoungStudents AS (
SELECT Name, Birthdate
FROM Students
WHERE Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD')
)
SELECT * FROM YoungStudents
ORDER BY Birthdate;
Kết hợp bảng (Joins)
INNER JOIN
Kết hợp bảng Students và Enrollments để lấy thông tin đăng ký:
SELECT s.Name, e.CourseID
FROM Students s
INNER JOIN Enrollments e ON s.StudentID = e.StudentID;
LEFT OUTER JOIN
Lấy tất cả sinh viên, kể cả chưa đăng ký khóa học:
SELECT s.Name, e.CourseID
FROM Students s
LEFT OUTER JOIN Enrollments e ON s.StudentID = e.StudentID;
RIGHT OUTER JOIN
Lấy tất cả đăng ký, kể cả sinh viên không tồn tại:
SELECT s.Name, e.CourseID
FROM Students s
RIGHT OUTER JOIN Enrollments e ON s.StudentID = e.StudentID;
FULL OUTER JOIN
Lấy tất cả sinh viên và đăng ký, kể cả không khớp:
SELECT s.Name, e.CourseID
FROM Students s
FULL OUTER JOIN Enrollments e ON s.StudentID = e.StudentID;
CROSS JOIN
Kết hợp tất cả dòng từ hai bảng (tích Descartes):
SELECT s.Name, c.CourseName
FROM Students s
CROSS JOIN Courses c;
SELF JOIN
Kết hợp bảng với chính nó để tìm sinh viên cùng ngày sinh:
SELECT s1.Name AS Student1, s2.Name AS Student2, s1.Birthdate
FROM Students s1
INNER JOIN Students s2 ON s1.Birthdate = s2.Birthdate
WHERE s1.StudentID != s2.StudentID;
Kết hợp nhiều bảng
Lấy thông tin sinh viên, khóa học và giáo viên:
SELECT s.Name AS StudentName, c.CourseName, t.Name AS TeacherName
FROM Students s
INNER JOIN Enrollments e ON s.StudentID = e.StudentID
INNER JOIN Courses c ON e.CourseID = c.CourseID
INNER JOIN Teachers t ON c.TeacherID = t.TeacherID;
UNION, UNION ALL, INTERSECT, MINUS
UNION: Hợp các tập hợp, loại bỏ trùng lặp.UNION ALL: Hợp các tập hợp, giữ trùng lặp.INTERSECT: Lấy giao của các tập hợp.MINUS: Lấy hiệu của các tập hợp (tương đươngEXCEPTtrong SQL Server).
Ví dụ:
SELECT Name FROM Students
WHERE Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD')
UNION
SELECT Name FROM Teachers
WHERE Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD');
Lấy sinh viên không đăng ký khóa học nào:
SELECT StudentID, Name
FROM Students
MINUS
SELECT s.StudentID, s.Name
FROM Students s
INNER JOIN Enrollments e ON s.StudentID = e.StudentID;
View
View là một bảng ảo được tạo từ một hoặc nhiều bảng cơ sở, dùng để đơn giản hóa truy vấn hoặc bảo mật dữ liệu.
Tạo View (CREATE VIEW)
Tạo view chứa thông tin sinh viên sinh sau năm 2000:
CREATE VIEW YoungStudents AS
SELECT StudentID, Name, Birthdate
FROM Students
WHERE Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD');
Tạo hoặc thay thế View (CREATE OR REPLACE VIEW)
Cập nhật view mà không cần xóa:
CREATE OR REPLACE VIEW YoungStudents AS
SELECT StudentID, Name, Birthdate
FROM Students
WHERE Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD')
WITH READ ONLY;
WITH READ ONLY: Ngăn chỉnh sửa dữ liệu qua view.
Sử dụng View
Truy vấn từ view như bảng thông thường:
SELECT * FROM YoungStudents
ORDER BY Birthdate;
Xóa View (DROP VIEW)
DROP VIEW YoungStudents;
View có thể cập nhật (Updatable View)
View đơn giản (không dùng hàm tổng hợp, join phức tạp) có thể được cập nhật:
CREATE VIEW SimpleStudents AS
SELECT StudentID, Name
FROM Students
WHERE Birthdate > TO_DATE('2000-01-01', 'YYYY-MM-DD');
UPDATE SimpleStudents
SET Name = 'New Name'
WHERE StudentID = 1;
Lưu ý: View phức tạp hơn cần trigger hoặc rule để cập nhật.
Liệt kê View
Xem tất cả view trong schema:
SELECT VIEW_NAME
FROM USER_VIEWS;
Index
Index tăng tốc độ truy vấn bằng cách tạo cấu trúc dữ liệu để tìm kiếm nhanh hơn.
Tạo Index (CREATE INDEX)
Tạo index trên cột Name của bảng Students:
CREATE INDEX idx_students_name
ON Students(Name);
Index duy nhất (UNIQUE INDEX)
Đảm bảo giá trị trong cột là duy nhất:
CREATE UNIQUE INDEX idx_students_email
ON Students(Email);
Index trên nhiều cột (Composite Index)
Tạo index trên Birthdate và Name:
CREATE INDEX idx_students_birthdate_name
ON Students(Birthdate, Name);
Xóa Index (DROP INDEX)
DROP INDEX idx_students_name;
Index Bitmap
Dùng cho cột có ít giá trị khác nhau (ví dụ: giới tính):
CREATE BITMAP INDEX idx_students_gender
ON Students(Gender);
Tự động tạo Index
Khi tạo PRIMARY KEY hoặc UNIQUE, Oracle tự động tạo index:
CREATE TABLE Students (
StudentID NUMBER PRIMARY KEY,
Name VARCHAR2(50)
);
- Index tự động được tạo cho
StudentID.
Liệt kê Index
Xem tất cả index trong schema:
SELECT INDEX_NAME, TABLE_NAME, COLUMN_NAME
FROM USER_IND_COLUMNS;
Khi nào nên dùng Index?
✅ Dùng khi:
- Cột thường xuyên xuất hiện trong
WHERE,JOIN, hoặcORDER BY. - Bảng lớn và truy vấn chọn lọc ít dòng.
❌ Không dùng khi:
- Bảng nhỏ (chi phí duy trì index cao hơn lợi ích).
- Cột thường xuyên cập nhật (index làm chậm
INSERT,UPDATE,DELETE).
Trigger
Trigger là đoạn mã PL/SQL tự động chạy khi có sự kiện (INSERT, UPDATE, DELETE) trên bảng.
Tạo Trigger (CREATE TRIGGER)
Trigger ghi log khi thêm sinh viên mới:
CREATE TABLE StudentsLog (
LogID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
StudentID NUMBER,
Action VARCHAR2(50),
ActionDate DATE
);
CREATE OR REPLACE TRIGGER trg_students_insert
AFTER INSERT ON Students
FOR EACH ROW
BEGIN
INSERT INTO StudentsLog (StudentID, Action, ActionDate)
VALUES (:NEW.StudentID, 'INSERT', SYSDATE);
END;
/
:NEW: Truy cập giá trị mới được chèn.FOR EACH ROW: Trigger chạy cho từng dòng.
Trigger trước sự kiện (BEFORE Trigger)
Kiểm tra tuổi trước khi thêm sinh viên:
CREATE OR REPLACE TRIGGER trg_students_before_insert
BEFORE INSERT ON Students
FOR EACH ROW
DECLARE
v_Age NUMBER;
BEGIN
v_Age := EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM :NEW.Birthdate);
IF v_Age < 16 THEN
RAISE_APPLICATION_ERROR(-20001, 'Sinh viên phải từ 16 tuổi trở lên.');
END IF;
END;
/
RAISE_APPLICATION_ERROR: Báo lỗi tùy chỉnh.
Trigger cập nhật (UPDATE Trigger)
Ghi log khi cập nhật tên sinh viên:
CREATE OR REPLACE TRIGGER trg_students_update
AFTER UPDATE OF Name ON Students
FOR EACH ROW
BEGIN
INSERT INTO StudentsLog (StudentID, Action, ActionDate)
VALUES (:OLD.StudentID, 'UPDATE: ' || :OLD.Name || ' to ' || :NEW.Name, SYSDATE);
END;
/
:OLD: Giá trị cũ trước khi cập nhật.:NEW: Giá trị mới sau khi cập nhật.
Trigger xóa (DELETE Trigger)
Ghi log khi xóa sinh viên:
CREATE OR REPLACE TRIGGER trg_students_delete
BEFORE DELETE ON Students
FOR EACH ROW
BEGIN
INSERT INTO StudentsLog (StudentID, Action, ActionDate)
VALUES (:OLD.StudentID, 'DELETE', SYSDATE);
END;
/
Trigger cấp bảng (Statement Trigger)
Chạy một lần cho toàn bộ sự kiện, không theo dòng:
CREATE OR REPLACE TRIGGER trg_students_statement
AFTER DELETE ON Students
BEGIN
INSERT INTO StudentsLog (StudentID, Action, ActionDate)
VALUES (NULL, 'Xóa nhiều sinh viên', SYSDATE);
END;
/
Vô hiệu hóa/Kích hoạt Trigger
- Vô hiệu hóa:
ALTER TRIGGER trg_students_insert DISABLE;
- Kích hoạt:
ALTER TRIGGER trg_students_insert ENABLE;
Xóa Trigger (DROP TRIGGER)
DROP TRIGGER trg_students_insert;
Liệt kê Trigger
Xem tất cả trigger trong schema:
SELECT TRIGGER_NAME, TABLE_NAME, TRIGGERING_EVENT
FROM USER_TRIGGERS;
Stored Procedure
Stored Procedure là tập hợp các câu lệnh PL/SQL được lưu trữ trong database để tái sử dụng.
Tạo Stored Procedure (CREATE PROCEDURE)
Tạo procedure thêm sinh viên mới:
CREATE OR REPLACE PROCEDURE AddStudent (
p_Name IN VARCHAR2,
p_Birthdate IN DATE
) AS
BEGIN
INSERT INTO Students (Name, Birthdate)
VALUES (p_Name, p_Birthdate);
DBMS_OUTPUT.PUT_LINE('Thêm sinh viên thành công: ' || p_Name);
END AddStudent;
/
IN: Tham số đầu vào (mặc định).ASthay choIScũng được chấp nhận.
Thực thi Stored Procedure (EXECUTE)
BEGIN
AddStudent('Hoàng Hiệp', TO_DATE('2001-05-20', 'YYYY-MM-DD'));
END;
/
Hoặc dùng cú pháp ngắn:
EXECUTE AddStudent('Hoàng Hiệp', TO_DATE('2001-05-20', 'YYYY-MM-DD'));
Stored Procedure với tham số đầu ra (OUT Parameter)
Tạo procedure trả về số lượng sinh viên:
CREATE OR REPLACE PROCEDURE GetStudentCount (
p_Year IN NUMBER,
p_Count OUT NUMBER
) AS
BEGIN
SELECT COUNT(*) INTO p_Count
FROM Students
WHERE EXTRACT(YEAR FROM Birthdate) = p_Year;
END GetStudentCount;
/
Thực thi:
DECLARE
v_Count NUMBER;
BEGIN
GetStudentCount(2001, v_Count);
DBMS_OUTPUT.PUT_LINE('Số sinh viên sinh năm 2001: ' || v_Count);
END;
/
Xóa Stored Procedure (DROP PROCEDURE)
DROP PROCEDURE AddStudent;
Liệt kê Stored Procedure
Xem tất cả procedure trong schema:
SELECT OBJECT_NAME
FROM USER_OBJECTS
WHERE OBJECT_TYPE = 'PROCEDURE';
Function
Function là một khối PL/SQL trả về một giá trị duy nhất.
Tạo Function (CREATE FUNCTION)
Tạo hàm tính tuổi từ ngày sinh:
CREATE OR REPLACE FUNCTION CalculateAge (
p_Birthdate IN DATE
) RETURN NUMBER AS
v_Age NUMBER;
BEGIN
v_Age := EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM p_Birthdate);
RETURN v_Age;
END CalculateAge;
/
Sử dụng Function
Dùng trong truy vấn SQL:
SELECT Name, CalculateAge(Birthdate) AS Age
FROM Students;
Function với logic phức tạp
Tạo hàm kiểm tra sinh viên có đủ tuổi:
CREATE OR REPLACE FUNCTION IsAdult (
p_StudentID IN NUMBER
) RETURN VARCHAR2 AS
v_Age NUMBER;
BEGIN
SELECT CalculateAge(Birthdate) INTO v_Age
FROM Students
WHERE StudentID = p_StudentID;
IF v_Age >= 18 THEN
RETURN 'Đủ tuổi';
ELSE
RETURN 'Chưa đủ tuổi';
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'Không tìm thấy sinh viên';
END IsAdult;
/
Thực thi:
SELECT Name, IsAdult(StudentID) AS Status
FROM Students;
Xóa Function (DROP FUNCTION)
DROP FUNCTION CalculateAge;
Liệt kê Function
Xem tất cả function trong schema:
SELECT OBJECT_NAME
FROM USER_OBJECTS
WHERE OBJECT_TYPE = 'FUNCTION';
Transaction
Transaction là tập hợp các thao tác được thực hiện như một đơn vị, đảm bảo tính toàn vẹn dữ liệu.
Bắt đầu Transaction
Oracle tự động bắt đầu transaction khi thực hiện lệnh INSERT, UPDATE, DELETE. Không cần BEGIN TRANSACTION như SQL Server.
Ví dụ:
INSERT INTO Students (Name, Birthdate)
VALUES ('Nguyễn Văn A', TO_DATE('2002-03-15', 'YYYY-MM-DD'));
UPDATE Students
SET Name = 'Nguyễn Văn B'
WHERE Name = 'Nguyễn Văn A';
Kết thúc Transaction
- COMMIT: Lưu vĩnh viễn các thay đổi.
COMMIT;
- ROLLBACK: Hủy bỏ các thay đổi.
ROLLBACK;
Ví dụ:
BEGIN
INSERT INTO Students (Name, Birthdate)
VALUES ('Trần Văn C', TO_DATE('2001-07-10', 'YYYY-MM-DD'));
-- Kiểm tra lỗi
IF SQL%ROWCOUNT = 1 THEN
COMMIT;
DBMS_OUTPUT.PUT_LINE('Thành công');
ELSE
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('Thất bại');
END IF;
END;
/
Điểm lưu (SAVEPOINT)
Đặt điểm lưu để rollback một phần:
BEGIN
INSERT INTO Students (Name, Birthdate)
VALUES ('Lê Thị D', TO_DATE('2000-12-25', 'YYYY-MM-DD'));
SAVEPOINT AfterInsert;
UPDATE Students
SET Name = 'Lê Thị E'
WHERE Name = 'Lê Thị D';
ROLLBACK TO AfterInsert; -- Quay lại sau khi insert
COMMIT;
END;
/
Transaction với exception
Xử lý lỗi trong transaction:
BEGIN
INSERT INTO Students (Name, Birthdate)
VALUES ('Phạm Văn F', TO_DATE('2003-01-01', 'YYYY-MM-DD'));
-- Giả lập lỗi
UPDATE Students
SET Name = NULL
WHERE StudentID = 1; -- Vi phạm NOT NULL
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('Lỗi: ' || SQLERRM);
END;
/
SQLERRM: Trả về thông báo lỗi.
Kiểm tra Transaction
Xem trạng thái transaction hiện tại không trực tiếp như SQL Server; dùng logic PL/SQL:
BEGIN
IF DBMS_TRANSACTION.STEP_ID IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE('Có transaction đang chạy');
ELSE
DBMS_OUTPUT.PUT_LINE('Không có transaction');
END IF;
END;
/
Autocommit
Mặc định, các công cụ như SQL*Plus hoặc SQL Developer không tự commit. Để bật autocommit:
SET AUTOCOMMIT ON;
Tắt:
SET AUTOCOMMIT OFF;
Cursor
Cursor là con trỏ dùng để xử lý từng dòng dữ liệu từ kết quả truy vấn trong PL/SQL.
Cursor ẩn (Implicit Cursor)
Oracle tự động tạo cursor cho các lệnh DML hoặc SELECT ... INTO:
BEGIN
UPDATE Students
SET Name = 'Updated Name'
WHERE StudentID = 1;
IF SQL%ROWCOUNT > 0 THEN
DBMS_OUTPUT.PUT_LINE('Đã cập nhật ' || SQL%ROWCOUNT || ' dòng');
ELSE
DBMS_OUTPUT.PUT_LINE('Không có dòng nào được cập nhật');
END IF;
END;
/
SQL%ROWCOUNT: Số dòng bị ảnh hưởng.SQL%FOUND: True nếu có dòng bị ảnh hưởng.
Cursor hiển nhiên (Explicit Cursor)
Dùng khi cần duyệt qua nhiều dòng.
Khai báo và sử dụng Cursor
DECLARE
CURSOR student_cursor IS
SELECT StudentID, Name, Birthdate
FROM Students
WHERE EXTRACT(YEAR FROM Birthdate) = 2001;
v_StudentID NUMBER;
v_Name VARCHAR2(50);
v_Birthdate DATE;
BEGIN
OPEN student_cursor;
LOOP
FETCH student_cursor INTO v_StudentID, v_Name, v_Birthdate;
EXIT WHEN student_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_StudentID || ' - ' || v_Name || ' - ' || TO_CHAR(v_Birthdate, 'DD/MM/YYYY'));
END LOOP;
CLOSE student_cursor;
END;
/
OPEN: Mở cursor.FETCH: Lấy dữ liệu vào biến.CLOSE: Đóng cursor.%NOTFOUND: Kiểm tra hết dữ liệu.
Cursor với vòng lặp FOR ( đơn giản hơn )
BEGIN
FOR rec IN (SELECT StudentID, Name, Birthdate
FROM Students
WHERE EXTRACT(YEAR FROM Birthdate) = 2001)
LOOP
DBMS_OUTPUT.PUT_LINE(rec.StudentID || ' - ' || rec.Name || ' - ' || TO_CHAR(rec.Birthdate, 'DD/MM/YYYY'));
END LOOP;
END;
/
- Không cần
OPEN,FETCH,CLOSEthủ công.
Cursor với tham số
DECLARE
CURSOR student_cursor (p_Year NUMBER) IS
SELECT StudentID, Name
FROM Students
WHERE EXTRACT(YEAR FROM Birthdate) = p_Year;
BEGIN
FOR rec IN student_cursor(2001)
LOOP
DBMS_OUTPUT.PUT_LINE(rec.StudentID || ' - ' || rec.Name);
END LOOP;
END;
/
Cursor cập nhật (FOR UPDATE)
Dùng để khóa dòng khi cập nhật:
DECLARE
CURSOR student_cursor IS
SELECT StudentID, Name
FROM Students
WHERE EXTRACT(YEAR FROM Birthdate) = 2001
FOR UPDATE;
BEGIN
FOR rec IN student_cursor
LOOP
UPDATE Students
SET Name = rec.Name || '_Updated'
WHERE CURRENT OF student_cursor;
END LOOP;
COMMIT;
END;
/
FOR UPDATE: Khóa dòng để tránh xung đột.WHERE CURRENT OF: Cập nhật dòng hiện tại của cursor.
Exception Handling
Exception Handling xử lý lỗi trong PL/SQL để tránh chương trình dừng đột ngột.
Cấu trúc cơ bản
BEGIN
-- Code có thể gây lỗi
INSERT INTO Students (Name, Birthdate)
VALUES (NULL, TO_DATE('2001-05-20', 'YYYY-MM-DD')); -- Vi phạm NOT NULL
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Lỗi: ' || SQLERRM);
END;
/
WHEN OTHERS: Bắt mọi lỗi không xác định.
Các exception thường gặp
| Exception | Ý nghĩa |
|---|---|
NO_DATA_FOUND | Không tìm thấy dữ liệu (SELECT ... INTO không trả về dòng nào) |
TOO_MANY_ROWS | SELECT ... INTO trả về nhiều dòng |
DUP_VAL_ON_INDEX | Vi phạm ràng buộc UNIQUE hoặc PRIMARY KEY |
VALUE_ERROR | Lỗi chuyển đổi dữ liệu (ví dụ: chuỗi thành số) |
OTHERS | Bắt mọi lỗi khác |
Ví dụ xử lý exception cụ thể
DECLARE
v_Name VARCHAR2(50);
BEGIN
SELECT Name INTO v_Name
FROM Students
WHERE StudentID = 999; -- Không tồn tại
DBMS_OUTPUT.PUT_LINE('Tên: ' || v_Name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Không tìm thấy sinh viên');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Trả về quá nhiều dòng');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Lỗi không xác định: ' || SQLERRM);
END;
/
Tự tạo exception
DECLARE
e_AgeTooLow EXCEPTION;
v_Age NUMBER;
BEGIN
SELECT EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM Birthdate) INTO v_Age
FROM Students
WHERE StudentID = 1;
IF v_Age < 16 THEN
RAISE e_AgeTooLow;
END IF;
EXCEPTION
WHEN e_AgeTooLow THEN
DBMS_OUTPUT.PUT_LINE('Tuổi quá thấp!');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Lỗi: ' || SQLERRM);
END;
/
PL/SQL Block
PL/SQL Block là khối mã cơ bản trong Oracle, gồm 3 phần: DECLARE, BEGIN, EXCEPTION.
Anonymous Block (Khối vô danh)
DECLARE
v_Count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_Count
FROM Students;
DBMS_OUTPUT.PUT_LINE('Tổng số sinh viên: ' || v_Count);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Lỗi: ' || SQLERRM);
END;
/
Nested Block (Khối lồng nhau)
BEGIN
DBMS_OUTPUT.PUT_LINE('Khối ngoài bắt đầu');
DECLARE
v_Name VARCHAR2(50);
BEGIN
SELECT Name INTO v_Name
FROM Students
WHERE StudentID = 1;
DBMS_OUTPUT.PUT_LINE('Tên trong khối lồng: ' || v_Name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Không tìm thấy trong khối lồng');
END;
DBMS_OUTPUT.PUT_LINE('Khối ngoài kết thúc');
END;
/
Debugging với DBMS_OUTPUT
Bật đầu ra trong SQL*Plus hoặc SQL Developer:
SET SERVEROUTPUT ON;
In thông tin:
BEGIN
DBMS_OUTPUT.PUT_LINE('Dòng 1');
DBMS_OUTPUT.PUT_LINE('Dòng 2');
END;
/
Ghi log lỗi với DBMS_UTILITY
BEGIN
INSERT INTO Students (Name) VALUES (NULL); -- Vi phạm NOT NULL
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;
/
FORMAT_ERROR_STACK: Thông tin lỗi.FORMAT_ERROR_BACKTRACE: Dấu vết lỗi (stack trace).
So sánh Oracle Database với SQL Server
Quản lý cơ sở dữ liệu:
- Oracle không dùng
CREATE DATABASEđơn giản như SQL Server mà yêu cầu cấu hình tệp dữ liệu (data files, redo logs). Tuy nhiên, DBCA giúp đơn giản hóa quá trình này. DROP DATABASEtrong Oracle xóa toàn bộ database, không chỉ là một schema.- Sao lưu và khôi phục dùng RMAN thay vì lệnh
BACKUPvàRESTORE. - Oracle không có lệnh
USEnhư SQL Server mà dùngALTER SESSION SET CURRENT_SCHEMA. - Liệt kê database trong Oracle phức tạp hơn, tập trung vào schema hoặc instance thay vì danh sách database.
- Oracle không dùng
Quản lý bảng:
- Oracle dùng
NUMBER GENERATED ALWAYS AS IDENTITYthay choINT IDENTITY(1,1)để tạo cột tự tăng. DROP TABLE PURGElà tùy chọn đặc trưng của Oracle để xóa vĩnh viễn.- Cú pháp
ALTER TABLEtrong Oracle khác biệt: dùngMODIFYthayALTER COLUMN,RENAME COLUMNthaysp_rename. - Liệt kê bảng dùng
USER_TABLEShoặcALL_TABLESthay choINFORMATION_SCHEMA.TABLES.
- Oracle dùng
Kiểu dữ liệu:
- Số: Oracle dùng
NUMBERthay choINT,DECIMAL,BIGINT,... Không cóMONEYhayBIT(dùngNUMBER(1)cho Boolean). - Chuỗi: Oracle dùng
VARCHAR2thayVARCHAR, vàCLOBthayTEXT(khác vớiVARCHAR(MAX)).TEXTkhông tồn tại trong Oracle. - Ngày giờ: Oracle không có
DATETIME2hayTIME, thay bằngTIMESTAMPvới độ chính xác cao hơn. - Nhị phân: Oracle dùng
BLOBthayVARBINARY(MAX), không cóUNIQUEIDENTIFIER(thay bằngRAWhoặc chuỗi GUID).
- Số: Oracle dùng
Biến:
- Oracle không có
DECLAREtrực tiếp trong SQL mà dùng trong khối PL/SQL. - Không có
SETnhư SQL Server; dùng:=hoặcSELECT ... INTO. - Biến bảng thay bằng collections (
VARRAY,TABLE OF) vớiBULK COLLECT. - Biến toàn cục như
@@ROWCOUNTthay bằngSQL%ROWCOUNT.
- Oracle không có
Hàm:
- Oracle dùng
LENGTHthayLEN,SUBSTRthaySUBSTRING,INSTRthayCHARINDEX. - Không có
TRIMriêng cho từng bên (LTRIM,RTRIM); chỉ cóTRIMchung. - Hàm ngày giờ khác biệt:
SYSDATEthayGETDATE,EXTRACTthayYEAR/MONTH/DAY. - Hàm chuyển đổi dùng
TO_CHAR,TO_NUMBER,TO_DATEthayCAST/CONVERT. NVLthayISNULL, nhưng tương tựCOALESCE.
- Oracle dùng
Ràng buộc dữ liệu:
- Oracle không có
IDENTITYmà dùngGENERATED ALWAYS AS IDENTITY. - Không có
DEFAULTnhư một ràng buộc riêng; giá trị mặc định khai báo trong định nghĩa cột. - Các ràng buộc như
FOREIGN KEY,CHECKtương tự, nhưng cú pháp hơi khác.
- Oracle không có
Thao tác dữ liệu:
- Oracle dùng
TO_DATEđể xử lý ngày giờ, thay vì định dạng trực tiếp như SQL Server. DELETEtrong Oracle có thể khôi phục từ Recycle Bin, không như SQL Server.TRUNCATE TABLEtương tự nhưng không có log chi tiết.CREATE TABLE AS SELECTthay choSELECT INTO.MERGEtrong Oracle không hỗ trợWHEN NOT MATCHED BY SOURCE THEN DELETEtrực tiếp.RETURNINGthay choOUTPUT, nhưng phải dùng trong PL/SQL.
- Oracle dùng
Toán tử và điều kiện lọc:
- Toán tử cơ bản (
=,>,LIKE,...) giống nhau. - Oracle không có
IIF; chỉ dùngCASE WHENhoặc logic trong PL/SQL. IF ELSEphải nằm trong khối PL/SQL, không dùng trực tiếp trong SQL như SQL Server.- Hàm ngày giờ như
EXTRACTthay choYEAR,MONTH,DAY.
- Toán tử cơ bản (
Truy vấn dữ liệu:
- Oracle không có
TOP; dùngFETCH FIRST(12c+) hoặcROWNUM(phiên bản cũ). - Alias cột trong Oracle có thể bỏ
AS, khác với SQL Server yêu cầu rõ ràng. - Hàm ngày giờ dùng
EXTRACTthayYEAR,MONTH,DAY. WITHclause tương đương CTE, nhưng cú pháp giống nhau.- Oracle không có
HAVINGmà không cầnGROUP BYnhư một số trường hợp trong SQL Server; luôn cầnGROUP BYtrướcHAVING.
- Oracle không có
Kết hợp bảng:
- Các loại
JOIN(INNER,LEFT,RIGHT,FULL,CROSS) tương tự SQL Server. - Oracle dùng
MINUSthayEXCEPTtrong SQL Server. - Cú pháp
JOINgiống nhau, nhưng Oracle cũng hỗ trợ cú pháp cũ(+), ví dụ:Tuy nhiên, nên dùng cú pháp ANSI (SELECT s.Name, e.CourseID FROM Students s, Enrollments e WHERE s.StudentID = e.StudentID(+); -- LEFT OUTER JOINJOIN) hiện đại.
- Các loại
View:
- Cú pháp
CREATE VIEWgiống nhau, nhưng Oracle cóWITH READ ONLYđể bảo vệ dữ liệu. - Oracle không có
SCHEMABINDINGnhư SQL Server; dùngWITH CHECK OPTIONnếu cần kiểm tra điều kiện khi cập nhật. - Liệt kê view dùng
USER_VIEWSthayINFORMATION_SCHEMA.VIEWS.
- Cú pháp
Index:
- Oracle tự động tạo index cho
PRIMARY KEYvàUNIQUE, giống SQL Server. - Có thêm
BITMAP INDEX(dành cho cột ít giá trị khác nhau), không có trong SQL Server. - Không có
INCLUDEnhư SQL Server để thêm cột không lập chỉ mục vào index.
- Oracle tự động tạo index cho
Trigger:
- Oracle dùng
:NEWvà:OLDthay@INSERTEDvà@DELETEDtrong SQL Server. - Trigger trong Oracle phân biệt rõ
FOR EACH ROW(dòng) và statement-level (bảng), trong khi SQL Server mặc định là statement-level. RAISE_APPLICATION_ERRORthayRAISERRORđể báo lỗi.- Oracle không có
INSTEAD OFtrigger trực tiếp như SQL Server; cần logic thay thế trongBEFOREtrigger.
- Oracle dùng
Stored Procedure:
- Oracle dùng
CREATE OR REPLACE PROCEDUREthayCREATE PROCEDURE(thay thế trực tiếp nếu tồn tại). - Tham số
IN,OUT,IN OUTrõ ràng hơn SQL Server (SQL Server chỉ có@param OUTPUT). - Không có
EXEC; dùngEXECUTEhoặc khốiBEGIN ... END.
- Oracle dùng
Function:
- Oracle yêu cầu
RETURNkiểu dữ liệu rõ ràng trong khai báo hàm. - Function có thể dùng trực tiếp trong SQL, tương tự SQL Server, nhưng cú pháp khai báo khác.
- Xử lý ngoại lệ (
EXCEPTION) tích hợp trong PL/SQL, mạnh mẽ hơn T-SQL.
- Oracle yêu cầu
Transaction:
- Oracle không cần
BEGIN TRANSACTION; transaction tự động bắt đầu với DML. COMMITvàROLLBACKtương tự, nhưng Oracle cóSAVEPOINTrõ ràng hơn SQL Server.- Xử lý lỗi dùng
EXCEPTION WHEN OTHERSthayTRY...CATCH. - Không có
@@TRANCOUNT; dùng góiDBMS_TRANSACTIONđể kiểm tra trạng thái.
- Oracle không cần
Cursor:
- Oracle phân biệt implicit (ẩn) và explicit (hiển nhiên) rõ ràng hơn SQL Server.
- Không có
FETCH NEXTnhư SQL Server; dùngFETCH INTOtrong vòng lặp hoặc vòngFOR. FOR UPDATEvàWHERE CURRENT OFlà cách khóa và cập nhật đặc trưng của Oracle.
Exception Handling:
- Oracle dùng
EXCEPTIONthayTRY...CATCH. - Các exception như
NO_DATA_FOUND,TOO_MANY_ROWScụ thể hơnERRORtrong SQL Server. RAISEvàRAISE_APPLICATION_ERRORthayTHROWđể tạo lỗi tùy chỉnh.
- Oracle dùng
PL/SQL Block:
- Oracle yêu cầu
/để kết thúc khối trong công cụ như SQL*Plus, không có trong SQL Server. - Khối lồng nhau và
DBMS_OUTPUTthay thếPRINTtrong T-SQL. - Không có
GOnhư SQL Server; mọi thứ trong một khối được xử lý liền mạch.
- Oracle yêu cầu
Bài tập thực hành
Hệ thống quản lý sinh viên tại trường đại học EZSE
Cơ sở dữ liệu dưới đây được thiết kế để quản lý thông tin về sinh viên, giảng viên, ngành học, môn học, và các lớp học tại trường đại học EZSE. Hệ thống hỗ trợ các chức năng như đăng ký môn học, theo dõi điểm số, và quản lý các lớp học.
Câu lệnh SQL tạo Database và dữ liệu
Trong Oracle, không có lệnh USE để chuyển database như SQL Server. Thay vào đó, bạn làm việc trong một schema (thường gắn với user). Tôi sẽ tạo schema EZSE và các bảng trong đó.
-- Kết nối với quyền SYSDBA để tạo user/schema (thực hiện một lần)
CONN SYS/your_password AS SYSDBA;
-- Xóa schema nếu đã tồn tại
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM dba_users WHERE username = 'EZSE';
IF v_count > 0 THEN
EXECUTE IMMEDIATE 'DROP USER EZSE CASCADE';
END IF;
END;
/
-- Tạo user/schema mới
CREATE USER EZSE IDENTIFIED BY ezse_password
DEFAULT TABLESPACE USERS
QUOTA UNLIMITED ON USERS;
-- Gán quyền cơ bản
GRANT CONNECT, RESOURCE TO EZSE;
-- Kết nối với schema EZSE
CONN EZSE/ezse_password;
-- Xóa các bảng nếu tồn tại (không cần kiểm tra khóa ngoại phức tạp như SQL Server)
BEGIN
FOR t IN (SELECT TABLE_NAME FROM USER_TABLES) LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || t.TABLE_NAME || ' PURGE';
END LOOP;
END;
/
---------------------------- Tạo bảng ----------------------------------
-- Bảng Ngành học (Majors)
CREATE TABLE Majors (
MajorID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
MajorName NVARCHAR2(100) NOT NULL
);
-- Bảng Sinh viên (Students)
CREATE TABLE Students (
StudentID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
FirstName NVARCHAR2(50) NOT NULL,
LastName NVARCHAR2(50) NOT NULL,
DateOfBirth DATE NOT NULL,
Gender NVARCHAR2(15) CHECK (Gender IN ('Male', 'Female', 'Other')),
Email NVARCHAR2(100) UNIQUE NOT NULL,
Phone NVARCHAR2(15) UNIQUE,
Address NVARCHAR2(255),
EnrollmentDate DATE NOT NULL,
MajorID NUMBER,
CONSTRAINT fk_students_majors FOREIGN KEY (MajorID) REFERENCES Majors(MajorID)
);
-- Bảng Giảng viên (Teachers)
CREATE TABLE Teachers (
TeacherID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
FirstName NVARCHAR2(50) NOT NULL,
LastName NVARCHAR2(50) NOT NULL,
Email NVARCHAR2(100) UNIQUE NOT NULL,
Phone NVARCHAR2(15) UNIQUE,
Address NVARCHAR2(255)
);
-- Bảng Môn học (Courses)
CREATE TABLE Courses (
CourseID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
CourseName NVARCHAR2(100) NOT NULL,
Credits NUMBER CHECK (Credits > 0)
);
-- Bảng Môn học theo ngành (MajorCourses)
CREATE TABLE MajorCourses (
MajorID NUMBER,
CourseID NUMBER,
PRIMARY KEY (MajorID, CourseID),
CONSTRAINT fk_majorcourses_majors FOREIGN KEY (MajorID) REFERENCES Majors(MajorID),
CONSTRAINT fk_majorcourses_courses FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);
-- Bảng Lớp học (Classes)
CREATE TABLE Classes (
ClassID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
CourseID NUMBER,
TeacherID NUMBER,
Semester NVARCHAR2(20) NOT NULL,
Year NUMBER NOT NULL,
CONSTRAINT fk_classes_courses FOREIGN KEY (CourseID) REFERENCES Courses(CourseID),
CONSTRAINT fk_classes_teachers FOREIGN KEY (TeacherID) REFERENCES Teachers(TeacherID)
);
-- Bảng Đăng ký môn học (Enrollments)
CREATE TABLE Enrollments (
StudentID NUMBER,
ClassID NUMBER,
Attempt NUMBER DEFAULT 1 NOT NULL,
Grade NUMBER(4,2) CHECK (Grade >= 0 AND Grade <= 10),
PRIMARY KEY (StudentID, ClassID, Attempt),
CONSTRAINT fk_enrollments_students FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
CONSTRAINT fk_enrollments_classes FOREIGN KEY (ClassID) REFERENCES Classes(ClassID)
);
#### Chèn dữ liệu vào các bảng
```sql
-- Chèn dữ liệu vào bảng Majors
INSERT INTO Majors (MajorName) VALUES
(N'Computer Science'),
(N'Software Engineering'),
(N'Information Security');
-- Chèn dữ liệu vào bảng Courses
INSERT INTO Courses (CourseName, Credits) VALUES
(N'Data Structures & Algorithms', 3),
(N'Object-Oriented Programming', 3),
(N'Database Systems', 3),
(N'Computer Networks', 3),
(N'Web Development', 4),
(N'Agile Software Development', 3),
(N'Cyber Security', 3),
(N'Mobile Application Development', 3);
-- Chèn dữ liệu vào bảng Students
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID) VALUES
(N'Phạm', N'Gia Bảo', TO_DATE('2002-03-10', 'YYYY-MM-DD'), 'Male', '[email protected]', '0912345678', N'12 Trần Phú, Hà Nội', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 1),
(N'Nguyễn', N'Thảo Linh', TO_DATE('2003-07-22', 'YYYY-MM-DD'), 'Female', '[email protected]', '0987654321', N'34 Lê Lợi, TP.HCM', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 2),
(N'Trần', N'Quang Vinh', TO_DATE('2001-05-15', 'YYYY-MM-DD'), 'Male', '[email protected]', '0933123456', N'56 Nguyễn Trãi, Đà Nẵng', TO_DATE('2020-09-01', 'YYYY-MM-DD'), 3),
(N'Lê', N'Minh Nhật', TO_DATE('2002-09-25', 'YYYY-MM-DD'), 'Male', '[email protected]', '0977890123', N'78 Hai Bà Trưng, Hải Phòng', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 1),
(N'Vũ', N'Hồng Ngọc', TO_DATE('2003-01-30', 'YYYY-MM-DD'), 'Female', '[email protected]', '0966543210', N'90 Quang Trung, Cần Thơ', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 2),
(N'Đỗ', N'Thanh Huyền', TO_DATE('2004-02-14', 'YYYY-MM-DD'), 'Female', '[email protected]', '0913456789', N'15 Hùng Vương, Huế', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 3),
(N'Hoàng', N'Đức Anh', TO_DATE('2003-11-05', 'YYYY-MM-DD'), 'Male', '[email protected]', '0934567890', N'27 Nguyễn Huệ, Quy Nhơn', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 1),
(N'Ngô', N'Khánh Duy', TO_DATE('2002-08-19', 'YYYY-MM-DD'), 'Male', '[email protected]', '0945678901', N'39 Lê Đại Hành, Vinh', TO_DATE('2020-09-01', 'YYYY-MM-DD'), 2),
(N'Bùi', N'Thục Đoan', TO_DATE('2004-06-25', 'YYYY-MM-DD'), 'Female', '[email protected]', '0956789012', N'51 Phạm Ngũ Lão, Nha Trang', TO_DATE('2023-09-01', 'YYYY-MM-DD'), 3),
(N'Phan', N'Tiến Đạt', TO_DATE('2003-04-12', 'YYYY-MM-DD'), 'Male', '[email protected]', '0967890123', N'63 Nguyễn Văn Cừ, Đà Lạt', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 1),
(N'Đặng', N'Mỹ Linh', TO_DATE('2004-09-08', 'YYYY-MM-DD'), 'Female', '[email protected]', '0978901234', N'75 Trần Hưng Đạo, Hà Nội', TO_DATE('2023-09-01', 'YYYY-MM-DD'), 2),
(N'Võ', N'Hoàng Phúc', TO_DATE('2002-12-30', 'YYYY-MM-DD'), 'Male', '[email protected]', '0989012345', N'87 Bạch Đằng, TP.HCM', TO_DATE('2020-09-01', 'YYYY-MM-DD'), 3),
(N'Trương', N'Ngọc Ánh', TO_DATE('2003-03-17', 'YYYY-MM-DD'), 'Female', '[email protected]', '0990123456', N'99 Nguyễn Thị Minh Khai, Đà Nẵng', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 1),
(N'Lý', N'Quốc Hưng', TO_DATE('2004-07-21', 'YYYY-MM-DD'), 'Male', '[email protected]', '0901234567', N'111 Lê Lai, Hải Phòng', TO_DATE('2023-09-01', 'YYYY-MM-DD'), 2),
(N'Nguyễn', N'Văn Hùng', TO_DATE('2003-05-10', 'YYYY-MM-DD'), 'Male', '[email protected]', '0912345689', N'123 Nguyễn Huệ, Hà Nội', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 1),
(N'Trần', N'Thị Mai', TO_DATE('2004-08-15', 'YYYY-MM-DD'), 'Female', '[email protected]', '0987654332', N'45 Lê Lợi, TP.HCM', TO_DATE('2023-09-01', 'YYYY-MM-DD'), 1),
(N'Lê', N'Quang Minh', TO_DATE('2002-11-20', 'YYYY-MM-DD'), 'Male', '[email protected]', '0933123467', N'67 Trần Phú, Đà Nẵng', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 1),
(N'Phạm', N'Hồng Phúc', TO_DATE('2003-02-25', 'YYYY-MM-DD'), 'Male', '[email protected]', '0977890134', N'89 Hai Bà Trưng, Hải Phòng', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 1),
(N'Vũ', N'Thị Lan', TO_DATE('2004-04-12', 'YYYY-MM-DD'), 'Female', '[email protected]', '0966543221', N'101 Quang Trung, Cần Thơ', TO_DATE('2023-09-01', 'YYYY-MM-DD'), 2),
(N'Đỗ', N'Minh Tuấn', TO_DATE('2003-09-30', 'YYYY-MM-DD'), 'Male', '[email protected]', '0913456790', N'23 Hùng Vương, Huế', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 2),
(N'Hoàng', N'Ngọc Ánh', TO_DATE('2002-07-18', 'YYYY-MM-DD'), 'Female', '[email protected]', '0934567901', N'34 Nguyễn Huệ, Quy Nhơn', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 2),
(N'Ngô', N'Văn Long', TO_DATE('2003-12-05', 'YYYY-MM-DD'), 'Male', '[email protected]', '0945678912', N'56 Lê Đại Hành, Vinh', TO_DATE('2022-09-01', 'YYYY-MM-DD'), 3),
(N'Bùi', N'Thị Hương', TO_DATE('2004-03-22', 'YYYY-MM-DD'), 'Female', '[email protected]', '0956789023', N'78 Phạm Ngũ Lão, Nha Trang', TO_DATE('2023-09-01', 'YYYY-MM-DD'), 3),
(N'Phan', N'Quốc Anh', TO_DATE('2002-06-15', 'YYYY-MM-DD'), 'Male', '[email protected]', '0967890134', N'90 Nguyễn Văn Cừ, Đà Lạt', TO_DATE('2021-09-01', 'YYYY-MM-DD'), 3);
-- Chèn dữ liệu vào bảng Teachers
INSERT INTO Teachers (FirstName, LastName, Email, Phone, Address) VALUES
(N'Hoàng', N'Văn Hiệp', '[email protected]', '0944123456', N'101 Lý Thường Kiệt, Hà Nội'),
(N'Lê', N'Thu Hà', '[email protected]', '0933221122', N'202 Phạm Văn Đồng, TP.HCM'),
(N'Hoàng', N'Việt Thắng', '[email protected]', '0922334455', N'303 Trường Chinh, Đà Nẵng'),
(N'Nguyễn', N'Minh Tuấn', '[email protected]', '0911445566', N'404 Nguyễn Văn Linh, Hải Phòng'),
(N'Phạm', N'Quốc Khánh', '[email protected]', '0902345678', N'505 Điện Biên Phủ, Huế'),
(N'Vũ', N'Thị Duyên', '[email protected]', '0913567890', N'606 Nguyễn Đình Chiểu, TP.HCM'),
(N'Trần', N'Đình Phong', '[email protected]', '0924678901', N'707 Trần Phú, Hà Nội'),
(N'Đỗ', N'Ngọc Mai', '[email protected]', '0935789012', N'808 Lê Hồng Phong, Đà Nẵng'),
(N'Bùi', N'Gia Bảo', '[email protected]', '0946890123', N'909 Nguyễn Trãi, Hải Phòng'),
(N'Ngô', N'Thị Lan Anh', '[email protected]', '0957901234', N'1010 Phạm Hùng, Cần Thơ');
-- Chèn dữ liệu vào bảng MajorCourses
INSERT INTO MajorCourses (MajorID, CourseID) VALUES
(1, 1), (1, 3), (1, 4), (1, 6),
(2, 2), (2, 5), (2, 6),
(3, 4), (3, 7), (3, 8);
-- Chèn dữ liệu vào bảng Classes
INSERT INTO Classes (CourseID, TeacherID, Semester, Year) VALUES
(1, 1, 'Spring', 2023),
(2, 2, 'Spring', 2023),
(3, 1, 'Summer', 2023),
(4, 3, 'Fall', 2023),
(5, 2, 'Spring', 2023),
(6, 4, 'Summer', 2023),
(7, 3, 'Fall', 2023),
(8, 5, 'Spring', 2023),
(1, 1, 'Spring', 2024),
(2, 2, 'Fall', 2024),
(3, 1, 'Spring', 2024),
(4, 3, 'Fall', 2024),
(5, 2, 'Summer', 2024),
(6, 4, 'Spring', 2024),
(7, 3, 'Fall', 2024),
(8, 5, 'Summer', 2024),
(1, 1, 'Fall', 2024),
(3, 6, 'Fall', 2024),
(1, 1, 'Spring', 2025),
(2, 2, 'Spring', 2025),
(3, 1, 'Summer', 2025),
(4, 3, 'Fall', 2025),
(5, 2, 'Spring', 2025),
(6, 4, 'Summer', 2025),
(7, 3, 'Fall', 2025),
(8, 5, 'Spring', 2025),
(2, 2, 'Fall', 2025),
(4, 3, 'Spring', 2025);
-- Chèn dữ liệu vào bảng Enrollments
INSERT INTO Enrollments (StudentID, ClassID, Attempt, Grade) VALUES
(1, 1, 1, 8.5),
(1, 9, 1, 7.2),
(1, 3, 1, 6.8),
(1, 4, 1, 5.5),
(1, 12, 2, 7.5),
(2, 2, 1, 8.0),
(2, 5, 1, 9.0),
(2, 6, 1, 6.0),
(2, 14, 1, 4.8),
(3, 4, 1, 7.5),
(3, 7, 1, 8.8),
(3, 8, 1, 9.2),
(4, 1, 1, 5.0),
(4, 9, 2, 7.2),
(4, 5, 1, 8.5),
(4, 6, 1, 9.1),
(5, 2, 1, 6.8),
(5, 3, 1, 5.5),
(5, 11, 2, 7.5),
(5, 8, 1, 8.3),
(6, 12, 1, 8.0),
(6, 15, 1, 7.8),
(6, 16, 1, 9.0),
(7, 9, 1, 7.5),
(7, 11, 1, 8.2),
(7, 12, 1, 6.5),
(8, 2, 1, 8.8),
(8, 5, 1, 7.0),
(8, 6, 1, 6.2),
(9, 20, 1, 8.5),
(9, 23, 1, 9.0),
(9, 16, 1, 7.8),
(10, 9, 1, 6.5),
(10, 11, 1, 7.0),
(10, 14, 1, 8.5),
(15, 10, 1, 7.8),
(19, 13, 1, 8.2),
(16, 17, 1, 6.5),
(20, 19, 1, 7.5),
(22, 24, 1, 8.5),
(21, 25, 1, 6.8),
(23, 26, 1, 7.2),
(24, 27, 1, 8.8);
-- Commit dữ liệu
COMMIT;
Các bảng trong Database
1. Bảng Majors (Ngành học)
Bảng này lưu thông tin về các ngành học tại trường.
CREATE TABLE Majors (
MajorID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
MajorName NVARCHAR2(100) NOT NULL
);
- MajorID: Mã ngành học, là khóa chính, tự động tăng.
- MajorName: Tên ngành học.
2. Bảng Students (Sinh viên)
Bảng này lưu trữ thông tin về sinh viên trong hệ thống.
CREATE TABLE Students (
StudentID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
FirstName NVARCHAR2(50) NOT NULL,
LastName NVARCHAR2(50) NOT NULL,
DateOfBirth DATE NOT NULL,
Gender NVARCHAR2(15) CHECK (Gender IN ('Male', 'Female', 'Other')),
Email NVARCHAR2(100) UNIQUE NOT NULL,
Phone NVARCHAR2(15) UNIQUE,
Address NVARCHAR2(255),
EnrollmentDate DATE NOT NULL,
MajorID NUMBER,
CONSTRAINT fk_students_majors FOREIGN KEY (MajorID) REFERENCES Majors(MajorID)
);
- StudentID: Mã sinh viên, là khóa chính, tự động tăng.
- FirstName, LastName: Tên và họ của sinh viên.
- DateOfBirth: Ngày sinh.
- Gender: Giới tính (Male, Female, Other).
- Email: Email của sinh viên, duy nhất.
- Phone: Số điện thoại, duy nhất (cho phép NULL).
- Address: Địa chỉ của sinh viên (cho phép NULL).
- EnrollmentDate: Ngày nhập học.
- MajorID: Mã ngành học của sinh viên, tham chiếu đến bảng
Majors.
3. Bảng Teachers (Giảng viên)
Bảng này lưu trữ thông tin về các giảng viên.
CREATE TABLE Teachers (
TeacherID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
FirstName NVARCHAR2(50) NOT NULL,
LastName NVARCHAR2(50) NOT NULL,
Email NVARCHAR2(100) UNIQUE NOT NULL,
Phone NVARCHAR2(15) UNIQUE,
Address NVARCHAR2(255)
);
- TeacherID: Mã giảng viên, là khóa chính, tự động tăng.
- FirstName, LastName: Tên và họ của giảng viên.
- Email: Email của giảng viên, duy nhất.
- Phone: Số điện thoại, duy nhất (cho phép NULL).
- Address: Địa chỉ của giảng viên (cho phép NULL).
4. Bảng Courses (Môn học)
Bảng này lưu thông tin về các môn học.
CREATE TABLE Courses (
CourseID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
CourseName NVARCHAR2(100) NOT NULL,
Credits NUMBER CHECK (Credits > 0)
);
- CourseID: Mã môn học, là khóa chính, tự động tăng.
- CourseName: Tên môn học.
- Credits: Số tín chỉ của môn học, lớn hơn 0.
5. Bảng MajorCourses (Môn học theo ngành)
Bảng này liên kết các ngành học với các môn học bắt buộc.
CREATE TABLE MajorCourses (
MajorID NUMBER,
CourseID NUMBER,
PRIMARY KEY (MajorID, CourseID),
CONSTRAINT fk_majorcourses_majors FOREIGN KEY (MajorID) REFERENCES Majors(MajorID),
CONSTRAINT fk_majorcourses_courses FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);
- MajorID: Mã ngành học, tham chiếu đến bảng
Majors. - CourseID: Mã môn học, tham chiếu đến bảng
Courses. - Khóa chính là tổ hợp
(MajorID, CourseID).
6. Bảng Classes (Lớp học)
Bảng này lưu thông tin về các lớp học.
CREATE TABLE Classes (
ClassID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
CourseID NUMBER,
TeacherID NUMBER,
Semester NVARCHAR2(20) NOT NULL,
Year NUMBER NOT NULL,
CONSTRAINT fk_classes_courses FOREIGN KEY (CourseID) REFERENCES Courses(CourseID),
CONSTRAINT fk_classes_teachers FOREIGN KEY (TeacherID) REFERENCES Teachers(TeacherID)
);
- ClassID: Mã lớp học, là khóa chính, tự động tăng.
- CourseID: Mã môn học, tham chiếu đến bảng
Courses. - TeacherID: Mã giảng viên, tham chiếu đến bảng
Teachers. - Semester: Học kỳ.
- Year: Năm học.
7. Bảng Enrollments (Đăng ký môn học)
Bảng này lưu trữ thông tin về việc sinh viên đăng ký lớp học.
CREATE TABLE Enrollments (
StudentID NUMBER,
ClassID NUMBER,
Attempt NUMBER DEFAULT 1 NOT NULL,
Grade NUMBER(4,2) CHECK (Grade >= 0 AND Grade <= 10),
PRIMARY KEY (StudentID, ClassID, Attempt),
CONSTRAINT fk_enrollments_students FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
CONSTRAINT fk_enrollments_classes FOREIGN KEY (ClassID) REFERENCES Classes(ClassID)
);
- StudentID: Mã sinh viên, tham chiếu đến bảng
Students. - ClassID: Mã lớp học, tham chiếu đến bảng
Classes. - Attempt: Số lần đăng ký (mặc định là 1).
- Grade: Điểm số, từ 0 đến 10.
Đề thực hành (Practical Exam)
Câu hỏi 1
Viết truy vấn để hiển thị tất cả các giảng viên.

Đáp án:
SELECT * FROM Teachers;
Câu hỏi 2
Viết truy vấn để hiển thị tất cả các sinh viên thuộc các ngành có MajorID = 2 hoặc MajorID = 3.

Đáp án:
SELECT * FROM Students
WHERE MajorID IN (2, 3);
Câu hỏi 3
Viết truy vấn để lấy thông tin các lớp học mà giảng viên có họ là "Hoàng" và tên là "Văn Hiệp" phụ trách.
Hiển thị các cột như hình dưới.

Đáp án:
SELECT C.ClassID, C.CourseID, CO.CourseName, C.Semester, C.Year, T.TeacherID, T.FirstName, T.LastName
FROM Classes C
JOIN Teachers T ON C.TeacherID = T.TeacherID
JOIN Courses CO ON C.CourseID = CO.CourseID
WHERE T.FirstName = N'Hoàng' AND T.LastName = N'Văn Hiệp';
Câu hỏi 4
Viết truy vấn để lấy danh sách sinh viên đã đăng ký ít nhất một trong các lớp thuộc các môn học "Data Structures & Algorithms", "Database Systems", hoặc "Web Development".
Hiển thị các cột như hình dưới, không hiển thị kết quả trùng nhau.
Kết quả sắp xếp theo CourseID tăng dần, sau đó theo StudentID tăng dần.

Đáp án:
SELECT DISTINCT CO.CourseID, CO.CourseName, S.StudentID, S.FirstName || ' ' || S.LastName AS StudentFullName, S.DateOfBirth
FROM Students S
JOIN Enrollments E ON S.StudentID = E.StudentID
JOIN Classes C ON E.ClassID = C.ClassID
JOIN Courses CO ON C.CourseID = CO.CourseID
WHERE CO.CourseName IN (N'Data Structures & Algorithms', N'Database Systems', N'Web Development')
ORDER BY CO.CourseID, S.StudentID;
Câu hỏi 5
Viết truy vấn để hiển thị số lượng sinh viên đã đăng ký vào các khóa học trong năm 2025.
Hiển thị các cột như hình dưới.
Kết quả sắp xếp theo NumberOfEnrollments giảm dần, sau đó theo CourseID tăng dần.

Đáp án:
SELECT C.CourseID, C.CourseName,
COUNT(E.StudentID) AS NumberOfEnrollments
FROM Courses C
LEFT JOIN Classes CL ON C.CourseID = CL.CourseID
LEFT JOIN Enrollments E ON CL.ClassID = E.ClassID AND CL.Year = 2025
GROUP BY C.CourseID, C.CourseName
ORDER BY NumberOfEnrollments DESC, C.CourseID ASC;
Câu hỏi 6
Viết truy vấn để tìm các giảng viên có số lượng lớp dạy nhiều nhất trong từng năm.
Hiển thị các cột như hình dưới.
Kết quả sắp xếp theo Year tăng dần, sau đó theo TeacherID tăng dần.

Đáp án:
WITH TeacherClasses AS (
SELECT
c.Year,
t.TeacherID,
t.FirstName || ' ' || t.LastName AS TeacherFullName,
COUNT(c.ClassID) AS NumberOfClasses
FROM Teachers t
LEFT JOIN Classes c ON t.TeacherID = c.TeacherID
GROUP BY c.Year, t.TeacherID, t.FirstName, t.LastName
),
MaxClasses AS (
SELECT
Year,
MAX(NumberOfClasses) AS MaxClasses
FROM TeacherClasses
GROUP BY Year
)
SELECT
tc.Year,
tc.TeacherID,
tc.TeacherFullName,
tc.NumberOfClasses
FROM TeacherClasses tc
JOIN MaxClasses mc ON tc.Year = mc.Year AND tc.NumberOfClasses = mc.MaxClasses
ORDER BY tc.Year, tc.TeacherID;
Câu hỏi 7
Viết truy vấn để đếm số sinh viên duy nhất đã đăng ký môn học theo từng năm của ngành "Computer Science" và "Software Engineering".
Hiển thị các cột như hình dưới.

Đáp án:
WITH StudentEnrollments AS (
SELECT
m.MajorID,
m.MajorName,
c.Year,
COUNT(DISTINCT e.StudentID) AS TotalStudents
FROM Enrollments e
JOIN Classes c ON e.ClassID = c.ClassID
JOIN Students s ON e.StudentID = s.StudentID
JOIN Majors m ON s.MajorID = m.MajorID
WHERE m.MajorName IN (N'Computer Science', N'Software Engineering')
GROUP BY m.MajorID, m.MajorName, c.Year
),
MajorsCTE AS (
SELECT MajorID, MajorName
FROM Majors
WHERE MajorName IN (N'Computer Science', N'Software Engineering')
),
Years AS (
SELECT DISTINCT Year FROM Classes
),
AllCombinations AS (
SELECT y.Year, m.MajorID, m.MajorName
FROM MajorsCTE m
CROSS JOIN Years y
)
SELECT
ac.Year,
ac.MajorID,
ac.MajorName,
NVL(se.TotalStudents, 0) AS TotalStudents
FROM AllCombinations ac
LEFT JOIN StudentEnrollments se
ON ac.MajorID = se.MajorID
AND ac.Year = se.Year
ORDER BY ac.Year, ac.MajorID;
Câu hỏi 8
Tạo function GetNumberOfStudentsByCourseID để tính số lượng sinh viên duy nhất đã đăng ký một môn học cụ thể theo CourseID.
Sau khi viết xong, kiểm tra lại function bằng câu lệnh SQL dưới:
SELECT CourseID, CourseName, GetNumberOfStudentsByCourseID(CourseID) AS NumberOfStudents
FROM Courses
WHERE CourseID IN (1, 2, 3, 4);

Đáp án:
CREATE OR REPLACE FUNCTION GetNumberOfStudentsByCourseID (p_CourseID IN NUMBER)
RETURN NUMBER AS
v_NumberOfStudents NUMBER;
BEGIN
SELECT COUNT(DISTINCT e.StudentID) INTO v_NumberOfStudents
FROM Enrollments e
JOIN Classes c ON e.ClassID = c.ClassID
WHERE c.CourseID = p_CourseID;
RETURN v_NumberOfStudents;
EXCEPTION
WHEN OTHERS THEN
RETURN 0; -- Trả về 0 nếu có lỗi
END GetNumberOfStudentsByCourseID;
/
-- Kiểm tra function
SELECT CourseID, CourseName, GetNumberOfStudentsByCourseID(CourseID) AS NumberOfStudents
FROM Courses
WHERE CourseID IN (1, 2, 3, 4);
Câu hỏi 9
Tạo trigger để xử lý dữ liệu khi thêm sinh viên mới vào bảng Students có tên trg_InsteadOfInsertStudent đảm bảo hai điều kiện:
- Nếu
Emailkhông chứa ký tự "@", tự động thêm@ezse.netvào cuối. - Nếu
MajorIDbị NULL, tự động đặtMajorIDthành ID của ngành "Software Engineering".
Sau khi viết xong, kiểm tra lại trigger bằng câu lệnh SQL dưới:
-- Trường hợp thiếu @ -> Tự động thêm @ezse.net, MajorID có giá trị nên giữ nguyên
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES (N'Nguyễn', N'Văn A', TO_DATE('2002-05-10', 'YYYY-MM-DD'), 'Male', 'nguyenvana', '0734567890', N'Hà Nội', TO_DATE('2024-04-02', 'YYYY-MM-DD'), 3);
-- Trường hợp thiếu MajorID -> Tự động gán MajorID của ngành Software Engineering
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES (N'Trần', N'Thị B', TO_DATE('2001-08-20', 'YYYY-MM-DD'), 'Female', '[email protected]', '0678123456', N'HCM', TO_DATE('2024-04-02', 'YYYY-MM-DD'), NULL);
-- Kiểm tra kết quả
SELECT StudentID, FirstName, LastName, Email, MajorID FROM Students;
-- Chú ý: Vì Phone là UNIQUE, khi test nhiều lần, hãy thay đổi số điện thoại.

Đáp án:
CREATE OR REPLACE TRIGGER trg_InsteadOfInsertStudent
BEFORE INSERT ON Students
FOR EACH ROW
DECLARE
v_SoftwareEngineeringID NUMBER;
BEGIN
-- Lấy MajorID của ngành "Software Engineering"
SELECT MajorID INTO v_SoftwareEngineeringID
FROM Majors
WHERE MajorName = N'Software Engineering';
-- Xử lý Email
IF INSTR(:NEW.Email, '@') = 0 THEN
:NEW.Email := :NEW.Email || '@ezse.net';
END IF;
-- Xử lý MajorID
IF :NEW.MajorID IS NULL THEN
:NEW.MajorID := v_SoftwareEngineeringID;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20001, 'Không tìm thấy ngành Software Engineering');
END trg_InsteadOfInsertStudent;
/
-- Kiểm tra trigger
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES (N'Nguyễn', N'Văn A', TO_DATE('2002-05-10', 'YYYY-MM-DD'), 'Male', 'nguyenvana', '0734567890', N'Hà Nội', TO_DATE('2024-04-02', 'YYYY-MM-DD'), 3);
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES (N'Trần', N'Thị B', TO_DATE('2001-08-20', 'YYYY-MM-DD'), 'Female', '[email protected]', '0678123456', N'HCM', TO_DATE('2024-04-02', 'YYYY-MM-DD'), NULL);
SELECT StudentID, FirstName, LastName, Email, MajorID FROM Students;
Câu hỏi 10
Xóa sinh viên có StudentID = 3 và StudentID = 5 khỏi bảng Students, đồng thời xóa các hàng liên quan trong bảng Enrollments.
Đáp án:
-- Xóa dữ liệu liên quan trong bảng Enrollments trước
DELETE FROM Enrollments
WHERE StudentID IN (3, 5);
-- Xóa sinh viên trong bảng Students
DELETE FROM Students
WHERE StudentID IN (3, 5);
-- Commit thay đổi
COMMIT;
Giải thích:
- Vì bảng
Enrollmentstrong script gốc không cóON DELETE CASCADE, ta phải xóa dữ liệu từEnrollmentstrước, sau đó xóa từStudents. - Nếu muốn tự động xóa, cần sửa bảng
Enrollmentskhi tạo:
CREATE TABLE Enrollments (
StudentID NUMBER,
ClassID NUMBER,
Attempt NUMBER DEFAULT 1 NOT NULL,
Grade NUMBER(4,2) CHECK (Grade >= 0 AND Grade <= 10),
PRIMARY KEY (StudentID, ClassID, Attempt),
CONSTRAINT fk_enrollments_students FOREIGN KEY (StudentID) REFERENCES Students(StudentID) ON DELETE CASCADE,
CONSTRAINT fk_enrollments_classes FOREIGN KEY (ClassID) REFERENCES Classes(ClassID)
);
Sau đó chỉ cần:
DELETE FROM Students WHERE StudentID IN (3, 5);
COMMIT;
So sánh với SQL Server
Tạo Database:
- SQL Server dùng
CREATE DATABASEvàUSE. Oracle không có khái niệm database riêng như vậy trong ngữ cảnh người dùng; thay vào đó, bạn tạo một schema (user) bằngCREATE USER. - Xóa database trong Oracle là xóa user với
DROP USER ... CASCADE.
- SQL Server dùng
Xóa bảng:
- SQL Server dùng
INFORMATION_SCHEMAđể kiểm tra và xóa khóa ngoại trước. Oracle đơn giản hóa bằng cách xóa tất cả bảng trong schema vớiDROP TABLE ... PURGE(xóa vĩnh viễn, không vào Recycle Bin).
- SQL Server dùng
Tạo bảng:
- Kiểu dữ liệu:
INTthay bằngNUMBER.NVARCHARthay bằngNVARCHAR2(hỗ trợ Unicode).DECIMAL(4,2)thay bằngNUMBER(4,2).
- IDENTITY: Oracle dùng
GENERATED ALWAYS AS IDENTITYthayIDENTITY(1,1). - Khóa ngoại: Oracle yêu cầu đặt tên rõ ràng (
CONSTRAINT fk_name) thay vì để mặc định như SQL Server. - CHECK: Giữ nguyên logic, nhưng cú pháp giống nhau.
- Kiểu dữ liệu:
Ngày tháng:
- SQL Server chấp nhận định dạng ngày trực tiếp (như
'2002-03-10'). Oracle yêu cầu dùngTO_DATEđể chuyển chuỗi thành kiểuDATE.
- SQL Server chấp nhận định dạng ngày trực tiếp (như
Kiểu dữ liệu:
NVARCHARtrong SQL Server thay bằngNVARCHAR2trong Oracle để hỗ trợ Unicode.DECIMAL(4,2)thay bằngNUMBER(4,2).
Transaction:
- SQL Server không cần
COMMITrõ ràng trong script này vì mặc định autocommit. Oracle yêu cầuCOMMITđể lưu dữ liệu vĩnh viễn.
- SQL Server không cần
Cú pháp INSERT:
- Giữ nguyên cú pháp
INSERT INTO ... VALUES, nhưng Oracle không cầnGOđể phân tách lệnh.
- Giữ nguyên cú pháp
Mô tả bảng:
- Thay
INT IDENTITY(1,1)bằngNUMBER GENERATED ALWAYS AS IDENTITY. NVARCHARthay bằngNVARCHAR2.DECIMAL(4,2)thay bằngNUMBER(4,2).- Khóa ngoại đặt tên rõ ràng bằng
CONSTRAINT.
- Thay
Truy vấn:
- Câu 1-2: Giữ nguyên cú pháp.
- Câu 3:
JOINgiống nhau. - Câu 4:
CONCAT(FirstName, ' ', LastName)trong SQL Server thay bằngFirstName || ' ' || LastNametrong Oracle.DISTINCTvàORDER BYgiống nhau.
- Câu 5:
LEFT JOINvàGROUP BYgiống nhau. - Câu 6:
CONCATthay bằng||để nối chuỗi.WITHclause (CTE) giống nhau.
- Câu 7:
COALESCEthay bằngNVL(tương tự nhưng Oracle dùngNVLphổ biến hơn).- Logic CTE giữ nguyên.
- Câu 8:
- Function trong Oracle dùng
CREATE OR REPLACE FUNCTION, trả về kiểuNUMBER. - Không cần
DECLAREriêng cho biến trong hàm; khai báo trực tiếp trong phầnAS. - Thêm
EXCEPTIONđể xử lý lỗi cơ bản.
- Function trong Oracle dùng
- Câu 9:
- SQL Server dùng
INSTEAD OF INSERT; Oracle không cóINSTEAD OFtrực tiếp, thay bằngBEFORE INSERTvà chỉnh sửa:NEW. NOT LIKE '%@%'thay bằngINSTR(:NEW.Email, '@') = 0.ISNULLthay bằngIS NULL.- Ngày dùng
TO_DATEthay vì định dạng trực tiếp.
- SQL Server dùng
- Câu 10:
- Oracle yêu cầu xóa thủ công từ bảng con (
Enrollments) trước nếu không cóON DELETE CASCADE. - Thêm
COMMITđể lưu thay đổi (SQL Server mặc định autocommit trong một số trường hợp).
- Oracle yêu cầu xóa thủ công từ bảng con (
