Phương pháp cơ bản và phổ biến nhất để kết hợp dữ liệu từ nhiều bảng là sử dụng phép toán JOIN. Một số người sử dụng JOIN như một mệnh đề riêng biệt trong câu lệnh SELECT, một số người sử dụng nó như một phần của mệnh đề FROM. Trước khi đi sâu vào từng loại JOIN, mình sẽ cung cấp cho các bạn những kiến thức về bản chất của JOIN và cách thức thực thi câu lệnh JOIN trong SQL.
Các nội dung chính
Mệnh đề FROM và bảng ảo
Nếu bạn đã tìm hiểu về thứ tự logic của các hoạt động được thực hiện khi SQL Server xử lý một truy vấn, thì bạn đã thấy rằng mệnh đề FROM của câu lệnh SELECT là mệnh đề đầu tiên được xử lý. Mệnh đề này xác định bảng hoặc các bảng nào sẽ là nguồn của các hàng cho truy vấn. FROM có thể tham chiếu đến một bảng hoặc kết hợp nhiều bảng làm nguồn dữ liệu cho truy vấn của bạn. Bạn có thể coi mệnh đề FROM giống như việc tạo và điền vào một bảng ảo. Bảng ảo này sẽ chứa đầu ra của mệnh đề FROM và được sử dụng bởi các mệnh đề của câu lệnh SELECT được áp dụng sau đó, chẳng hạn như mệnh đề WHERE. Bạn có thể hiểu là mục đích của các phần tử mệnh đề FROM là thêm hàng vào hoặc xóa hàng khỏi bảng ảo.
Bảng ảo được tạo bởi mệnh đề FROM chỉ là một thực thể logic. Trong SQL Server, không có bảng vật lý nào được tạo, cho dù liên tục hay tạm thời, để lưu giữ kết quả của mệnh đề FROM, vì nó được chuyển đến mệnh đề WHERE hoặc các phần khác của truy vấn.
Bảng ảo được tạo bởi mệnh đề FROM chứa dữ liệu từ tất cả các bảng đã nối. Bạn có thể coi các kết quả dưới dạng tập hợp và sơ đồ hóa các kết quả nối dưới dạng biểu đồ Venn.

Cú pháp JOIN
Thông thường, để lấy ra các dòng với điều kiện lọc là ModelID giống nhau ở 2 bảng Product và ProductModel, chúng ta sẽ viết câu lệnh truy vấn như sau:
SELECT p.ProductID
, m.Name AS Model
, p.Name AS Product
FROM SalesLT.Product AS p
, SalesLT.ProductModel AS m
WHERE p.ProductModelID = m.ProductModelID;
Cú pháp này vẫn được SQL Server hỗ trợ, nhưng quá phức tạp. Ngoài ra, nếu mệnh đề WHERE vô tình bị bỏ qua, các phép nối kiểu ANSI SQL-89 có thể dễ dàng trở thành tích Descartes và trả về quá nhiều hàng kết quả, gây ra các vấn đề về hiệu suất và có thể là kết quả không chính xác.
Lưu ý: Chúng ta sẽ cần phải hiểu một chút về tích Descartes (Đề các). Trong toán học, tích Đề-các là tích của hai tập hợp. Tích của một tập hợp hai phần tử và một tập hợp sáu phần tử là một tập hợp 12 phần tử, hay 6 x 2. Mọi phần tử trong một tập hợp này được kết hợp với mọi phần tử trong tập hợp kia. Trong ví dụ dưới đây, chúng ta có một tập hợp tên có hai phần tử và một tập hợp các sản phẩm có ba phần tử. Tích Descartes kết hợp 2 tên với 3 sản phẩm tạo ra sáu dòng như sau.

Trong cơ sở dữ liệu, tích Đề-các là kết quả của việc kết hợp mọi hàng trong một bảng với mọi hàng của bảng khác. Tích của bảng có 10 hàng và bảng có 100 hàng là kết quả của tập hợp có 1.000 hàng. Kết quả cơ bản của một phép toán JOIN là một tích Descartes nhưng đối với hầu hết các truy vấn T-SQL, đây không phải là kết quả mong muốn.
Với sự ra đời của tiêu chuẩn ANSI SQL-92, hỗ trợ cho các từ khóa JOIN và các mệnh đề ON đã được thêm vào. T-SQL cũng hỗ trợ cú pháp này. Các phép join được biểu diễn trong mệnh đề FROM bằng cách sử dụng toán tử JOIN thích hợp. Mối quan hệ logic giữa các bảng, trở thành một vị từ bộ lọc, được chỉ định trong mệnh đề ON.
Ví dụ sau đây trình bày lại truy vấn trước đó bằng cú pháp mới hơn:
SELECT p.ProductID
, m.Name AS Model
, p.Name AS Product
FROM SalesLT.Product AS p
, SalesLT.ProductModel AS m
WHERE p.ProductModelID = m.ProductModelID
Lưu ý: Cú pháp ANSI SQL-92 gây khó khăn hơn trong việc tạo các tích Descartes ngẫu nhiên. Khi từ khóa JOIN đã được thêm vào, lỗi cú pháp sẽ phát sinh nếu thiếu mệnh đề ON, trừ khi JOIN được chỉ định là CROSS JOIN.
SELECT p.ProductID
, m.Name AS Model
, p.Name AS Product
FROM SalesLT.Product AS p JOIN SalesLT.ProductModel AS m ON p.ProductModelID = m.ProductModelID
Các loại JOIN trong SQL bao gồm:
- INNER JOIN
- OUTER JOIN
- LEFT JOIN
- RIGHT JOIN
- CROSS JOIN
- SELF JOIN
Tìm hiểu về INNER JOIN
Các inner joins được sử dụng để giải quyết nhiều vấn đề kinh doanh phổ biến, đặc biệt là trong môi trường cơ sở dữ liệu được chuẩn hóa cao. Để truy xuất dữ liệu đã được lưu trữ trên nhiều bảng, bạn thường sẽ cần kết hợp dữ liệu đó thông qua các truy vấn INNER JOIN. INNER JOIN bắt đầu giai đoạn xử lý logic của nó như một tích Descartes, sau đó được lọc để loại bỏ bất kỳ hàng nào không khớp với vị từ.
Cách thực thi INNER JOIN
Chúng ta hãy kiểm tra các bước mà SQL Server sẽ xử lý một cách hợp lý một truy vấn JOIN.
SELECT emp.FirstName, ord.Amount
FROM HR.Employee AS emp
JOIN Sales.SalesOrder AS ord ON emp.EmployeeID = ord.EmployeeID;
Như bạn đã biết, mệnh đề FROM sẽ được xử lý trước mệnh đề SELECT. Hãy theo dõi quá trình xử lý, bắt đầu với dòng 2:
- Mệnh đề FROM chỉ định bảng HR.Employee là một trong các bảng đầu vào, đặt cho nó bí danh là emp.
- Toán tử JOIN trong dòng 3 phản ánh việc sử dụng INNER JOIN (kiểu mặc định trong T-SQL) và chỉ định Sales.SalesOrder là bảng đầu vào khác, có bí danh là ord.
- SQL Server sẽ thực hiện phép nối Descartes hợp lý trên các bảng này và chuyển kết quả dưới dạng bảng ảo cho bước tiếp theo.
- Sử dụng mệnh đề ON, SQL Server sẽ lọc bảng ảo, chỉ giữ lại những hàng mà giá trị EmployeeID từ bảng emp khớp với một EmployeeID trong bảng ord.
- Các hàng còn lại được giữ nguyên trong bảng ảo và được chuyển sang bước tiếp theo trong câu lệnh SELECT. Trong ví dụ này, bảng ảo được xử lý tiếp theo bởi mệnh đề SELECT và ba cột được chỉ định được trả về ứng dụng khách.
Kết quả của truy vấn là danh sách nhân viên và số lượng đơn đặt hàng của họ. Những nhân viên không có bất kỳ đơn hàng liên quan nào cũng như bất kỳ đơn hàng nào không có ID Nhân viên đều đã bị lọc ra bởi ON.

Cú pháp của INNER JOIN
INNER JOIN là loại JOIN mặc định và từ khóa INNER tùy chọn được ẩn trong mệnh đề JOIN như sau:
SELECT emp.FirstName
, ord.Amount
FROM HR.Employee AS emp
INNER JOIN Sales.SalesOrder AS ord ON emp.EmployeeID = ord.EmployeeID
Khi viết các truy vấn bằng cách sử dụng các INNER JOINS, hãy xem xét các nguyên tắc sau:
- Bí danh bảng được ưu tiên, không chỉ cho danh sách SELECT, mà còn để viết mệnh đề ON.
- Các inner join có thể được thực hiện trên một cột đối sánh duy nhất, chẳng hạn như OrderID hoặc trên nhiều thuộc tính đối sánh, chẳng hạn như sự joins của OrderID và ProductID. Các joins chỉ định nhiều cột phù hợp được gọi là phép nối hỗn hợp (composite join).
- Thứ tự các bảng được liệt kê trong mệnh đề FROM đối với INNER JOIN không quan trọng đối với trình tối ưu hóa SQL Server. Về mặt khái niệm, các joins sẽ được đánh giá từ trái sang phải.
- Sử dụng từ khóa JOIN một lần cho mỗi cặp bảng đã joins trong danh sách FROM. Đối với truy vấn hai bảng, chúng ta sử dụng một join. Đối với truy vấn ba bảng, bạn sẽ sử dụng JOIN hai lần; một lần giữa hai bảng đầu tiên và một lần nữa giữa đầu ra của JOIN giữa hai bảng đầu tiên và bảng thứ ba.
Ví dụ về INNER JOIN
Ví dụ giả định sau đây thực hiện một joins trên một cột đối sánh duy nhất, sử dụng ProductModelID trong bảng Production.Product với ProductModelID tại bảng Production.ProductModel:
SELECT
p.ProductID
, m.Name AS Model
, p.Name AS Product
FROM Production.Product AS p
INNER JOIN Production.ProductModel AS m ON p.ProductModelID = m.ProductModelID
ORDER BY p.ProductID
Ví dụ tiếp theo này cho thấy cách một inner join có thể được mở rộng để bao gồm nhiều hơn hai bảng. Bảng Sales.SalesOrderDetail được joins với đầu ra của JOIN giữa Production.Product và Production.ProductModel. Mỗi phiên bản của JOIN / ON thực hiện tập hợp và lọc bảng đầu ra ảo của riêng nó. Trình tối ưu hóa truy vấn SQL Server xác định thứ tự mà joins và lọc sẽ được thực hiện.
SELECT od.SalesOrderID
, m.Name AS Model
, p.Name AS ProductName
, od.OrderQty
FROM Production.Product AS p
INNER JOIN Production.ProductModel AS m ON p.ProductModelID = m.ProductModelID
INNER JOIN Sales.SalesOrderDetail AS od ON p.ProductID = od.ProductID ORDER BY od.SalesOrderID
Tìm hiểu về OUTER JOIN
Cách thực thi OUTER JOIN
Trước đây, bạn đã học cách sử dụng INNER JOIN để tìm các hàng phù hợp giữa hai bảng. Như bạn đã thấy, bộ xử lý truy vấn xây dựng kết quả của truy vấn INNER JOIN bằng cách lọc ra các hàng không đáp ứng các điều kiện được thể hiện trong vị từ mệnh đề ON. Kết quả là chỉ các hàng có một hàng phù hợp trong bảng khác được trả về. Với THAM GIA NGOÀI TRỜI, bạn có thể chọn hiển thị tất cả các hàng có các hàng khớp giữa các bảng, cộng với tất cả các hàng không khớp trong bảng khác.
Đầu tiên, hãy xem truy vấn sau, được viết bằng INNER JOIN:
SELECT emp.FirstName, ord.Amount
FROM HR.Employee AS emp
INNER JOIN Sales.SalesOrder AS ord ON emp.EmployeeID = ord.EmployeeID;
Các hàng này thể hiện sự phù hợp giữa HR.Employee và Sales.SalesOrder. Chỉ những giá trị EmployeeID có trong cả hai bảng mới xuất hiện trong kết quả truy vấn.

Bây giờ, hãy xem truy vấn sau, được viết là LEFT OUTER JOIN:
SELECT emp.FirstName, ord.Amount
FROM HR.Employee AS emp
LEFT OUTER JOIN Sales.SalesOrder AS ord ON emp.EmployeeID = ord.EmployeeID
Ví dụ này sử dụng toán tử LEFT OUTER JOIN để giữ lại tất cả các hàng từ bảng bên trái HR.Employee và hiển thị giá trị Amount cho các hàng phù hợp trong Sales.SalesOrder. Tuy nhiên, tất cả nhân viên đều được trả lại, cho dù họ có nhận đơn hàng bán hay không. Thay cho giá trị Amount, truy vấn sẽ trả về NULL cho nhân viên không có đơn đặt hàng bán hàng phù hợp.

Cú pháp OUTER JOIN
Các outer join được thể hiện bằng cách sử dụng các từ khóa LEFT, RIGHT hoặc FULL đứng trước OUTER JOIN. Mục đích của từ khóa là chỉ ra bảng nào (ở phía nào của từ khóa JOIN) nên được giữ nguyên và hiển thị tất cả các hàng của nó; phù hợp, hoặc không khớp.
Khi sử dụng LEFT, RIGHT hoặc FULL để xác định một phép nối, bạn có thể bỏ qua từ khóa OUTER như được hiển thị ở đây:
SELECT
emp.FirstName
, ord.Amount
FROM HR.Employee AS emp
LEFT JOIN Sales.SalesOrder AS ord ON emp.EmployeeID = ord.EmployeeID
Ngoài ra, khi viết truy vấn bằng OUTER JOIN, bạn nên tuân thủ môt số nguyên tắc sau đây để câu lệnh của bạn dễ hiểu, dễ đọc, và dễ dàng sửa lỗi nếu có:
- Bí danh bảng không chỉ được ưu tiên cho danh sách SELECT mà còn cho mệnh đề ON.
- Giống như với INNER JOIN, OUTER JOIN có thể được thực hiện trên một cột đối sánh duy nhất hoặc trên nhiều thuộc tính phù hợp.
- Khác với INNER JOIN, khi sử dụng OUTER JOIN, bạn cần phải quan tâm đến thứ tự các bảng được liệt kê và join trong mệnh đề FROM vì nó sẽ xác định xem bạn chọn LEFT hay RIGHT cho phép join của mình.
- Các phép nối nhiều bảng phức tạp hơn khi có OUTER JOIN. Sự hiện diện của NULL trong kết quả của OUTER JOIN có thể gây ra sự cố nếu kết quả trung gian sau đó được nối với một bảng thứ ba. Các hàng có NULL có thể được lọc ra bởi vị từ của phép nối thứ hai.
- Để chỉ hiển thị các hàng không khớp nhau, hãy thêm kiểm tra NULL trong mệnh đề WHERE theo sau vị từ OUTER JOIN.
- Thông thường chúng ta hiếm khi sử dụng FULL OUTER JOIN. Nó trả về tất cả các hàng phù hợp giữa hai bảng, cộng với tất cả các hàng từ bảng đầu tiên không khớp trong bảng thứ hai, cộng với tất cả các hàng trong bảng thứ hai không có khớp trong bảng đầu tiên.
- Bạn phải sử dụng ORDER BY để chỉ định thứ tự các hàng được trả về.
Tìm hiểu về CROSS JOINS
Một phép cross join bản chất là một tích Descartes của hai bảng. Nói cách khác, phép cross join sẽ tạo ra một tập hợp tất cả các kết quả có thể có khi kết hợp các hàng của bảng này với các hàng của bảng còn lại.
Câu lệnh dưới đây tạo ra một tập hợp kết quả với tất cả các kết hợp có thể có của các hàng đầu vào:
SELECT <select_list>
FROM table1 AS t1
CROSS JOIN table2 AS t2;
CROSS JOIN có một số ứng dụng nhất định như sau:
- Tạo một bảng số, với một hàng cho mỗi giá trị có thể có trong một phạm vi.
- Tạo khối lượng lớn dữ liệu để thử nghiệm. Khi được kết hợp chéo với chính nó, một bảng có ít nhất 100 hàng có thể dễ dàng tạo ra 10.000 hàng đầu ra mà bạn không cần phải tốn quá nhiều sức lực
Ví dụ CROSS JOIN
Truy vấn sau đây là một ví dụ về việc sử dụng CROSS JOIN để tạo tất cả các tổ hợp nhân viên và sản phẩm:
SELECT emp.FirstName, prd.Name
FROM HR.Employee AS emp
CROSS JOIN Production.Product AS prd
Tìm hiểu về SELF JOIN
Không chỉ join giữa nhiều bảng khác nhau, trong một số tình huống, có thể chúng ta sẽ cần truy xuất và so sánh các hàng trong cùng một bảng. Ví dụ: trong nhân sự, bảng Employee (Nhân viên) có thể bao gồm thông tin về người quản lý của từng nhân viên và lưu trữ ID của người quản lý trong hàng của chính nhân viên đó. Mỗi người quản lý cũng được liệt kê là một nhân viên.
EmployeeID | FirstName | ManagerID |
1 | Dan | NULL |
2 | Aisha | 1 |
3 | Rosie | 1 |
4 | Naomi | 3 |
Để truy xuất thông tin nhân viên và khớp nó với người quản lý có liên quan, bạn có thể sử dụng bảng hai lần trong truy vấn của mình, kết hợp nó với chính nó cho các mục đích của truy vấn.
SELECT emp.FirstName AS Employee
, mgr.FirstName AS Manager
FROM HR.Employee AS emp
LEFT OUTER JOIN HR.Employee AS mgr ON emp.ManagerID = mgr.EmployeeID;
Kết quả của truy vấn này bao gồm một hàng cho mỗi nhân viên với tên của người quản lý của họ. Giám đốc điều hành của công ty không có người quản lý. Để đưa CEO vào kết quả, một phép outer join được sử dụng và tên người quản lý được trả về dưới dạng NULL cho các hàng mà trường ManagerID không có trường EmployeeID phù hợp.
Employee | Manager |
Dan | NULL |
Aisha | Dan |
Rosie | Dan |
Naomi | Rosie |
Như vậy chúng ta đã đi qua các loại JOIN phổ biến và cả ít phổ biến trong SQL. Nếu cảm thấy hữu ích, đừng quên chia sẻ bài viết này tới bạn bè, đồng nghiệp của bạn cũng đang tự học SQL nhé.
Các bạn có thể tham khảo thêm về khóa học SQL của Datapot tại đây nhé.