Mệnh đề UNION được sử dụng để ghép kết quả của hai hoặc nhiều câu lệnh SELECT thành một tập kết quả duy nhất theo chiều dọc (nối thêm các dòng dữ liệu).
Các câu lệnh SELECT tham gia UNION phải trả về cùng số lượng cột, các cột ở cùng vị trí nên có kiểu dữ liệu tương thích và có cùng ý nghĩa.
Cú pháp
SELECT cột1, cột2, ... FROM bảng1
UNION
SELECT cột1, cột2, ...FROM bảng2
Các cách sử dụng
| Cách sử dụng | Mô tả | Ví dụ |
|---|---|---|
| Ghép hai bảng có cùng cấu trúc | Kết hợp dữ liệu từ hai bảng có cùng số lượng cột. | SELECT * FROM [Data2025$] UNION SELECT * FROM [Data2026$] |
| Ghép các cột được chọn | Chỉ ghép các cột cần thiết thay vì toàn bộ dữ liệu. | SELECT [Mã KH], [Tên KH] FROM [Customer1$] UNION SELECT [Mã KH], [Tên KH] FROM [Customer2$] |
| Sắp xếp kết quả | Chỉ sử dụng một mệnh đề ORDER BY ở cuối câu lệnh UNION. |
... UNION ... ORDER BY [Mã KH] |
Lưu ý
| Nội dung | Ví dụ |
|---|---|
Tất cả các câu lệnh SELECT phải có cùng số lượng cột. |
SELECT [Mã], [Tên] ... UNION SELECT [Mã], [Tên] ... |
| Các cột ở cùng vị trí nên có kiểu dữ liệu tương thích. | Cột thứ nhất đều là mã, cột thứ hai đều là tên. |
Tên cột của kết quả được lấy theo câu lệnh SELECT đầu tiên. |
SELECT [Mã KH], [Tên KH] ... UNION SELECT [Code], [Customer] ... → Kết quả có tiêu đề là Mã KH, Tên KH. |
Chỉ được sử dụng một mệnh đề ORDER BY và phải đặt ở cuối toàn bộ câu lệnh UNION. |
... UNION ... ORDER BY [Tên KH] |
Không sử dụng ORDER BY trong từng câu lệnh SELECT riêng lẻ. |
Đúng: SELECT ... UNION SELECT ... ORDER BY [Mã] |
Có thể sử dụng WHERE, GROUP BY và HAVING trong từng câu lệnh SELECT. |
SELECT ... WHERE ... GROUP BY ... HAVING ... UNION SELECT ... |
So sánh UNION và UNION ALL
| Câu lệnh | Kết quả trả về |
|---|---|
UNION |
Kết hợp kết quả và tự động loại bỏ các dòng trùng lặp. |
UNION ALL |
Kết hợp kết quả và giữ lại tất cả các dòng, kể cả các dòng trùng lặp. Thực hiện nhanh hơn UNION do không phải kiểm tra trùng lặp. |
Ví dụ 1: Ghép dữ liệu từ hai worksheet
SELECT [Mã KH], [Tên KH], [Doanh thu] FROM [Customer2025$]
UNION
SELECT [Mã KH], [Tên KH], [Doanh thu] FROM [Customer2026$]
Ví dụ 2: Giữ lại tất cả các bản ghi
SELECT [Mã SP], [Tên SP] FROM [ProductsA$]
UNION ALL
SELECT [Mã SP], [Tên SP] FROM [ProductsB$]
Ví dụ 3: Kết hợp với ORDER BY
SELECT [Mã KH], [Tên KH] FROM [CustomerHN$]
UNION
SELECT [Mã KH], [Tên KH] FROM [CustomerHCM$]
ORDER BY [Tên KH]
Mệnh đề JOIN được sử dụng để kết hợp dữ liệu từ hai hoặc nhiều bảng (Worksheet hoặc Named Range) dựa trên một hoặc nhiều trường có liên quan.
Trong SQL VBA Excel (Jet/ACE SQL), các bảng được liên kết thông qua mệnh đề ON, trong đó điều kiện liên kết thường là các trường có cùng ý nghĩa, chẳng hạn như Mã nhân viên, Mã khách hàng, Mã sản phẩm,...
Cú pháp
SELECT danh_sách_cột
FROM bảng1
INNER JOIN bảng2
ON điều_kiện_liên_kết
Các kiểu JOIN
| Kiểu JOIN | Mô tả | Ví dụ |
|---|---|---|
INNER JOIN |
Chỉ trả về các bản ghi có dữ liệu khớp ở cả hai bảng. | FROM [Employees$] INNER JOIN [Departments$] ON [Employees$].[Mã PB] = [Departments$].[Mã PB] |
LEFT JOIN hoặc LEFT OUTER JOIN |
Trả về toàn bộ dữ liệu của bảng bên trái và các bản ghi khớp của bảng bên phải. Nếu không có dữ liệu khớp, các trường của bảng bên phải sẽ có giá trị NULL. |
FROM [Employees$] LEFT JOIN [Departments$] ON [Employees$].[Mã PB] = [Departments$].[Mã PB] |
RIGHT JOIN hoặc RIGHT OUTER JOIN |
Trả về toàn bộ dữ liệu của bảng bên phải và các bản ghi khớp của bảng bên trái. | FROM [Employees$] RIGHT JOIN [Departments$] ON [Employees$].[Mã PB] = [Departments$].[Mã PB] |
Lưu ý: SQL Excel không hỗ trợ
FULL OUTER JOIN.
Các kiểu JOIN
| Kiểu JOIN | Mô tả | Kết quả trả về | Ví dụ |
|---|---|---|---|
INNER JOIN |
Chỉ trả về các bản ghi có dữ liệu khớp ở cả hai bảng. | Chỉ các bản ghi có giá trị liên kết tồn tại đồng thời ở bảng bên trái và bảng bên phải. | FROM [Employees$] INNER JOIN [Departments$] ON [Employees$].[Mã PB] = [Departments$].[Mã PB] |
LEFT JOIN hoặc LEFT OUTER JOIN |
Trả về toàn bộ dữ liệu của bảng bên trái và các bản ghi khớp của bảng bên phải. | Tất cả các bản ghi của bảng bên trái. Nếu không tìm thấy bản ghi tương ứng ở bảng bên phải thì các trường của bảng bên phải có giá trị NULL. |
FROM [Employees$] LEFT JOIN [Departments$] ON [Employees$].[Mã PB] = [Departments$].[Mã PB] |
RIGHT JOIN hoặc RIGHT OUTER JOIN |
Trả về toàn bộ dữ liệu của bảng bên phải và các bản ghi khớp của bảng bên trái. | Tất cả các bản ghi của bảng bên phải. Nếu không tìm thấy bản ghi tương ứng ở bảng bên trái thì các trường của bảng bên trái có giá trị NULL. |
FROM [Employees$] RIGHT JOIN [Departments$] ON [Employees$].[Mã PB] = |
Lưu ý
| Nội dung | Ví dụ |
|---|---|
| Khi hai bảng có cùng tên trường, nên chỉ rõ tên bảng trước tên trường. | [Employees$].[Mã PB] |
Tên Worksheet trong mệnh đề FROM và JOIN phải đặt trong dấu ngoặc vuông ([]) và kết thúc bằng ký tự $. |
[Employees$] |
Có thể sử dụng Worksheet hoặc Named Range trong câu lệnh JOIN. |
INNER JOIN [DepartmentList] ON ... |
Điều kiện liên kết được khai báo trong mệnh đề ON. |
ON [Orders$].[Mã KH] = [Customers$].[Mã KH] |
Có thể kết hợp thêm WHERE, GROUP BY, HAVING và ORDER BY sau khi thực hiện JOIN. |
... WHERE ... GROUP BY ... ORDER BY ... |
Nếu tên trường có khoảng trắng hoặc ký tự tiếng Việt, phải đặt trong dấu ngoặc vuông ([]). |
[Ngày giao hàng] |
Ví dụ 1: INNER JOIN
Lấy danh sách nhân viên cùng tên phòng ban.
SELECT [Employees$].[Mã NV],[Employees$].[Họ tên],[Departments$].[Tên phòng]
FROM [Employees$]
INNER JOIN [Departments$]
ON [Employees$].[Mã PB] = [Departments$].[Mã PB]
Ví dụ 2: LEFT JOIN
Lấy toàn bộ nhân viên, kể cả những nhân viên chưa được phân phòng ban.
SELECT [Employees$].[Mã NV],[Employees$].[Họ tên],[Departments$].[Tên phòng]
FROM [Employees$]
LEFT JOIN [Departments$]
ON [Employees$].[Mã PB] = [Departments$].[Mã PB]
Ví dụ 3: JOIN kết hợp GROUP BY
Thống kê số nhân viên của từng phòng ban.
SELECT [Departments$].[Tên phòng],COUNT(*) AS [Số nhân viên]
FROM [Departments$]
INNER JOIN [Employees$]
ON [Departments$].[Mã PB] = [Employees$].[Mã PB]
GROUP BY [Departments$].[Tên phòng]
ORDER BY COUNT(*) DESC
Câu lệnh SQL lồng nhau (Subquery) là một câu lệnh SELECT được đặt bên trong một câu lệnh SQL khác. Kết quả của câu lệnh bên trong sẽ được sử dụng làm điều kiện hoặc nguồn dữ liệu cho câu lệnh bên ngoài.
Trong SQL VBA Excel (Jet/ACE SQL), Subquery thường được sử dụng trong các mệnh đề WHERE, FROM và với các toán tử như IN, EXISTS, ANY, ALL.
Cú pháp
SELECT danh_sách_cột
FROM bảng
WHERE biểu_thức toán_tử
(SELECT ...)
Các cách sử dụng
| Cách sử dụng | Mô tả | Ví dụ |
|---|---|---|
Subquery trong WHERE |
Sử dụng kết quả của truy vấn con để lọc dữ liệu. | SELECT * FROM [Employees$] WHERE [Mã PB] IN (SELECT [Mã PB] FROM [Departments$] WHERE [Khu vực]='Miền Bắc') |
Subquery với IN |
Kiểm tra giá trị có thuộc tập kết quả của truy vấn con hay không. | WHERE [Mã KH] IN (SELECT [Mã KH] FROM [Orders$]) |
Subquery với NOT IN |
Loại bỏ các giá trị có trong kết quả truy vấn con. | WHERE [Mã KH] NOT IN (SELECT [Mã KH] FROM [Orders$]) |
Subquery với EXISTS |
Kiểm tra truy vấn con có trả về bản ghi hay không. | WHERE EXISTS (SELECT * FROM [Orders$] WHERE [Orders$].[Mã KH]=[Customers$].[Mã KH]) |
Subquery trong FROM |
Sử dụng kết quả của một truy vấn làm bảng tạm để truy vấn tiếp. | SELECT * FROM (SELECT [Mã KH], SUM([Doanh thu]) AS [Tổng DT] FROM [Sales$] GROUP BY [Mã KH]) |
Subquery với ANY |
So sánh với ít nhất một giá trị trong tập kết quả của truy vấn con. | WHERE [Lương] > ANY (SELECT [Lương] FROM [Employees$] WHERE [Phòng ban]='Kế toán') |
Subquery với ALL |
So sánh với tất cả các giá trị trong tập kết quả của truy vấn con. | WHERE [Lương] > ALL (SELECT [Lương] FROM [Employees$] WHERE [Phòng ban]='Kế toán') |
Lưu ý
| Nội dung | Ví dụ |
|---|---|
Truy vấn con phải được đặt trong dấu ngoặc đơn (). |
WHERE [Mã KH] IN (SELECT [Mã KH] FROM [Orders$]) |
Truy vấn con trong IN hoặc NOT IN chỉ nên trả về một cột. |
SELECT [Mã KH] FROM [Orders$] |
Khi sử dụng toán tử so sánh (=, >, <, >=, <=, <>), truy vấn con phải trả về một giá trị duy nhất. |
WHERE [Lương] > (SELECT AVG([Lương]) FROM [Employees$]) |
| Có thể sử dụng hàm tổng hợp trong truy vấn con. | SELECT AVG([Lương]) FROM [Employees$] |
Có thể kết hợp WHERE, GROUP BY và HAVING trong truy vấn con. |
SELECT [Mã PB] FROM [Employees$] GROUP BY [Mã PB] HAVING COUNT(*) >= 10 |
Nếu truy vấn con trả về nhiều dòng nhưng toán tử chỉ chấp nhận một giá trị (=, >, <...), câu lệnh sẽ phát sinh lỗi. Trong trường hợp này nên sử dụng IN, ANY hoặc ALL. |
WHERE [Mã PB] IN (SELECT [Mã PB] FROM [Departments$]) |
Ví dụ 1: Subquery với IN
Lấy danh sách khách hàng đã phát sinh đơn hàng.
SELECT * FROM [Customers$] WHERE [Mã KH] IN
(
SELECT [Mã KH] FROM [Orders$]
)
Ví dụ 2: Subquery với hàm tổng hợp
Lấy các nhân viên có lương cao hơn mức lương trung bình.
SELECT * FROM [Employees$] WHERE [Lương] >
(
SELECT AVG([Lương]) FROM [Employees$]
)
Ví dụ 3: Subquery với EXISTS
Lấy các khách hàng có ít nhất một đơn hàng.
SELECT *
FROM [Customers$]
WHERE EXISTS
(
SELECT *
FROM [Orders$]
WHERE [Orders$].[Mã KH] = [Customers$].[Mã KH]
)
Ví dụ 4: Subquery trong FROM
Tính tổng doanh thu theo khách hàng, sau đó chỉ lấy các khách hàng có tổng doanh thu lớn hơn 100.000.000.
SELECT *
FROM
(
SELECT [Mã KH],
SUM([Doanh thu]) AS [Tổng doanh thu]
FROM [Sales$]
GROUP BY [Mã KH]
) AS Q
WHERE [Tổng doanh thu] > 100000000
| Toán tử | Ý nghĩa | Điều kiện trả về TRUE |
Ví dụ |
|---|---|---|---|
EXISTS |
Kiểm tra truy vấn con có trả về ít nhất một bản ghi hay không. | Truy vấn con trả về từ 1 bản ghi trở lên. | WHERE EXISTS (SELECT * FROM [Orders$] WHERE [Orders$].[Mã KH] = [Customers$].[Mã KH]) |
NOT EXISTS |
Kiểm tra truy vấn con không trả về bản ghi nào. | Truy vấn con không trả về bản ghi. | WHERE NOT EXISTS (SELECT * FROM [Orders$] WHERE [Orders$].[Mã KH] = [Customers$].[Mã KH]) |
ANY (SOME) |
So sánh với từng giá trị trong tập kết quả của truy vấn con. | Điều kiện đúng với ít nhất một giá trị trong tập kết quả. | WHERE [Lương] > ANY (SELECT [Lương] FROM [Employees$] WHERE [Phòng ban]='Kế toán') |
ALL |
So sánh với tất cả các giá trị trong tập kết quả của truy vấn con. | Điều kiện đúng với mọi giá trị trong tập kết quả. | WHERE [Lương] > ALL (SELECT [Lương] FROM [Employees$] WHERE [Phòng ban]='Kế toán') |
Ví dụ minh họa
Giả sử truy vấn con trả về các giá trị:
10
20
30
| Điều kiện | Kết quả | Giải thích |
|---|---|---|
25 > ANY (10,20,30) |
TRUE |
Đúng vì 25 > 10 hoặc 25 > 20. Chỉ cần đúng với một giá trị. |
25 > ALL (10,20,30) |
FALSE |
Sai vì 25 không lớn hơn 30. Phải đúng với tất cả các giá trị. |
35 > ALL (10,20,30) |
TRUE |
Đúng vì 35 lớn hơn 10, 20 và 30. |
5 > ANY (10,20,30) |
FALSE |
Sai vì 5 không lớn hơn bất kỳ giá trị nào trong tập kết quả. |
EXISTS (SELECT ...) |
TRUE |
Truy vấn con trả về ít nhất một bản ghi. |
NOT EXISTS (SELECT ...) |
TRUE |
Truy vấn con không trả về bản ghi nào. |
Lưu ý
ANY và ALL chỉ sử dụng với truy vấn con (Subquery), không sử dụng với danh sách giá trị trực tiếp.
ANY tương đương với việc điều kiện đúng với ít nhất một giá trị trong tập kết quả.
ALL yêu cầu điều kiện đúng với tất cả các giá trị trong tập kết quả.
EXISTS và NOT EXISTS chỉ kiểm tra sự tồn tại của bản ghi, không quan tâm giá trị của các cột được chọn trong truy vấn con.
Trong SQL VBA Excel (Jet/ACE SQL), EXISTS, NOT EXISTS, ANY và ALL đều được sử dụng cùng với truy vấn con và đặt trong mệnh đề WHERE.
| SQL Server | Giải thích ý nghĩa | Hỗ trợ trong SQL Excel | Cách chuyển đổi | Ví dụ chuyển đổi |
|---|---|---|---|---|
CASE WHEN ... THEN ... END |
Phân nhánh điều kiện, trả về giá trị theo từng điều kiện. | ❌ Không hỗ trợ | Sử dụng IIF() hoặc lồng nhiều IIF(). |
SQL Server: CASE WHEN [Điểm]>=5 THEN 'Đạt' ELSE 'Trượt' ENDSQL Excel: IIF([Điểm]>=5,'Đạt','Trượt') |
ISNULL(expr, value) |
Thay thế giá trị NULL bằng giá trị khác. |
❌ Không hỗ trợ | Sử dụng NZ(expr,value) (nếu hỗ trợ) hoặc IIF(expr IS NULL,value,expr). |
SQL Server: ISNULL([Ghi chú],'')SQL Excel: IIF([Ghi chú] IS NULL,'',[Ghi chú]) |
COALESCE(expr1, expr2,...) |
Trả về giá trị khác NULL đầu tiên trong danh sách. |
❌ Không hỗ trợ | Dùng NZ() hoặc lồng nhiều IIF(). |
SQL Server: COALESCE([Email],[Điện thoại],'N/A')SQL Excel: IIF([Email] IS NOT NULL,[Email],IIF([Điện thoại] IS NOT NULL,[Điện thoại],'N/A')) |
TRY_CAST() |
Chuyển đổi kiểu dữ liệu, nếu lỗi sẽ trả về NULL. |
❌ Không hỗ trợ | Kiểm tra dữ liệu trước khi chuyển đổi hoặc xử lý bằng VBA. | Thực hiện kiểm tra dữ liệu trước khi gọi CINT(), CDATE()... |
TRY_CONVERT() |
Chuyển đổi kiểu dữ liệu theo định dạng, nếu lỗi trả về NULL. |
❌ Không hỗ trợ | Kiểm tra dữ liệu trước khi chuyển đổi hoặc xử lý bằng VBA. | Thực hiện trong VBA trước khi chạy SQL. |
CAST() |
Chuyển đổi kiểu dữ liệu. | ⚠️ Hỗ trợ một phần | Sử dụng CINT(), CLNG(), CDBL(), CDATE(), CSTR()... |
SQL Server: CAST([SL] AS INT)SQL Excel: CINT([SL]) |
CONVERT() |
Chuyển đổi kiểu dữ liệu và định dạng hiển thị. | ❌ Không hỗ trợ | Sử dụng các hàm chuyển đổi kiểu của VBA/Access. | SQL Server: CONVERT(DATE,[Ngày])SQL Excel: CDATE([Ngày]) |
FORMAT() |
Định dạng số, ngày tháng hoặc chuỗi theo mẫu. | ⚠️ Hỗ trợ một phần | Sử dụng FORMAT() theo cú pháp của chương trình (nếu hỗ trợ). |
FORMAT([Doanh thu],'#,##0') |
STRING_AGG() |
Ghép nhiều giá trị thành một chuỗi. | ❌ Không hỗ trợ | Thực hiện bằng VBA hoặc xử lý sau truy vấn. | Không có câu lệnh SQL tương đương. |
CONCAT_WS() |
Ghép chuỗi có ký tự phân cách. | ❌ Không hỗ trợ | Sử dụng CONCAT() hoặc toán tử nối chuỗi. |
SQL Server: CONCAT_WS('-',A,B)SQL Excel: CONCAT(A,'-',B) |
LEFT() |
Lấy một số ký tự từ bên trái chuỗi. | ⚠️ Hỗ trợ một phần | Dùng SUBSTR() hoặc hàm tương đương. |
SQL Server: LEFT([Mã],3)SQL Excel: SUBSTR([Mã],1,3) |
RIGHT() |
Lấy một số ký tự từ bên phải chuỗi. | ⚠️ Hỗ trợ một phần | Dùng SUBSTR() kết hợp LEN() hoặc hàm tương đương. |
SQL Server: RIGHT([Mã],3)SQL Excel: SUBSTR([Mã],LEN([Mã])-2,3) |
CHARINDEX() |
Tìm vị trí của chuỗi con trong chuỗi. | ❌ Không hỗ trợ | Sử dụng INSTR(). |
SQL Server: CHARINDEX('-',[Mã])SQL Excel: INSTR([Mã],'-') |
PATINDEX() |
Tìm vị trí chuỗi theo mẫu (LIKE). |
❌ Không hỗ trợ | Sử dụng LIKE hoặc INSTR(). |
Tùy từng trường hợp tìm kiếm. |
GETDATE() |
Trả về ngày giờ hiện tại của hệ thống. | ❌ Không hỗ trợ | Sử dụng NOW(). |
SQL Server: GETDATE()SQL Excel: NOW() |
CURRENT_TIMESTAMP |
Trả về ngày giờ hiện tại. | ⚠️ Hỗ trợ một phần | Sử dụng NOW(). |
NOW() |
ROW_NUMBER() |
Đánh số thứ tự cho các bản ghi. | ❌ Không hỗ trợ | Đánh số bằng VBA hoặc sau khi truy vấn. | Thêm cột STT sau khi lấy dữ liệu. |
RANK() |
Xếp hạng các bản ghi, có thể có hạng trùng. | ❌ Không hỗ trợ | Xử lý bằng VBA. | Thực hiện tính thứ hạng sau khi truy vấn. |