Join (SQL)

Một từ khóa **join ** kết nối nhiều bản ghi từ 2 bảng dữ liệu trong 1 cơ sở dữ liệu quan hệ và kết quả được đưa vào một bảng (tạm). Trong ngôn ngữ truy vấn theo cấu trúc (SQL) có ba loại kết hợp sau: nội, ngoại và chéo. Kết ngoại được chia ra thêm thành kết ngoại bên trái (left outer join), kết ngoại bên phải (right outer join), và kết ngoại đủ (full outer join).

Về mặt toán học, phép kết hợp là 1 cấu tạo quan hệ, 1 thao tác cơ bản trong đại số quan hệ và tổng quát hóa và là khái quát hóa của cấu tạo hàm.

## Ví dụ trên bảng

Những ví dụ dưới đây về phép kết hợp sử dụng hai bảng sau:

Để minh họa, ta cần chú ý rằng DepartmentID của nhân viên Jasper không có dòng tương ứng nào trong bảng department, và phòng Marketing thì không có dòng tương ứng trong bảng employee.

## Các hình thức kết hợp

### Kết nội

Một **phép kết nội** (inner join) thực chất là tìm giao của hai bảng dữ liệu. Đây là loại kết hợp thường được dùng nhất và được xem như là phép kết hợp *mặc định*. Ví dụ dưới đây về phép kết hợp trích tất cả các bản ghi trong bảng dữ liệu A (employee) và tìm những bản ghi tương ứng trong bảng B (department). Nếu không tìm được, những bản ghi trong A sẽ không được đưa vào kết quả. Nếu có nhiều kết quả tìm được trong B tương ứng với vị từ, thì 1 dòng sẽ được trả về ứng với mỗi vị từ (giá trị ở bảng A sẽ được lặp).

Cần đặc biệt chú ý khi kết hợp những bảng trên những cột mà có thể là NULL vì giá trị NULL sẽ không bao giờ có tương ứng. Xem giải pháp ở Phép Kết Ngoại Trái và Phép Kết Ngoại phải

Ví dụ về phép kết nội (cú pháp chuẩn ANSI 92):
 SELECT * 
 FROM employee 
 INNER JOIN department 
 ON employee.DepartmentID = department.DepartmentID
Ví dụ về phép kết nội (cú pháp không chuẩn):
 SELECT * 
 FROM employee, department 
 WHERE employee.DepartmentID = department.DepartmentID

Kết quả kết hợp:
 +-----------+--------------+----------------+--------------+
 | LastName | DepartmentID | DepartmentName | DepartmentID |
 +-----------+--------------+----------------+--------------+
 | Smith |  34 | Clerical  |  34 |
 | Jones |  33 | Engineering  |  33 |
 | Robinson |  34 | Clerical  |  34 |
 | Steinberg |  33 | Engineering  |  33 |
 | Rafferty |  31 | Sales   |  31 |
 +-----------+--------------+----------------+--------------+

Chú ý rằng nhân viên Jasper và phòng Marketing không xuất hiện. Không một bản nào trong những bản này có dòng tương ứng trong bảng kết hợp của chúng, nên đã bị loại ra khỏi kết quả của phép kết nội này.

### Kết ngoại bên trái

Một **phép kết ngoại bên trái** (left outer join) khác rất nhiều với 1 phép kết nội. Thay vì giới hạn những kết quả thu được trong cả hai bảng, nó chỉ giới hạn đối với những kết quả ở bảng bên trái (A). Nghĩa là nếu mệnh đề ON không có bản ghi tương ứng bên bảng B, 1 dòng trong kết quả vẫn được trả về nhưng với giá trị NULL cho mỗi cột trong bảng B.

**Nó trả về tất cả những giá trị từ bản bên trái + những giá trị tương ứng với bảng bên phải hoặc là null (khi những giá trị ở bảng bên phải không tương ứng)**

Chẳng hạn, nó cho phép ta tìm phòng ban của nhân viên, nhưng vẫn trả về nhân viên ngay cả khi phòng ban của người ấy là NULL hoặc không tồn tại. Ví dụ ở trên sẽ bỏ qua những nhân viên trong phòng ban không tồn tại.

Ví dụ về phép kết ngoại bên trái (cú pháp chuẩn ANSI 92):
 SELECT distinct * 
 FROM employee 
 LEFT OUTER JOIN 
 department 
  ON employee.DepartmentID = department.DepartmentID
Ví dụ về phép kết ngoại bên trái (cú pháp không chuẩn):
 SELECT *
 FROM employee, department
 WHERE employee.DepartmentID = department.DepartmentID(+)

 +-----------+--------------+----------------+--------------+
 | LastName | DepartmentID | DepartmentName | DepartmentID |
 +-----------+--------------+----------------+--------------+
 | Smith |  34 | Clerical  |  34 |
 | Jones |  33 | Engineering  |  33 |
 | Robinson |  34 | Clerical  |  34 |
 | Jasper  |  36 | NULL  |   NULL |
 | Steinberg |  33 | Engineering  |  33 |
 | Rafferty |  31 | Sales   |  31 |
 +-----------+--------------+----------------+--------------+

Trong cú pháp không chuẩn, dấu (+) ngụ ý rằng bảng department phải được mở rộng (bằng giá trị NULL) cho những dòng không tương ứng tồn tại trong bảng employee.
Dấu (+) chỉ được dùng trong một số hệ quản trị CSDL, một số hệ quản trị CSDL không hiểu dấu (+). Sau đây là mã lỗi (Server: Msg 170, Level 15, State 1, Line 3
Line 3: Incorrect syntax near ')')

### Kết ngoại bên phải

Một *phép kết ngoại bên phải* (right outer join) hầu như tương tự với phép kết ngoại bên trái, trừ 1 điều là thứ tự các bảng đổi lại. Mỗi bản ghi từ bảng bên phải, B hoặc department sẽ được trả về và giá trị NULL sẽ được trả về cho những dòng mà không có bản ghi tương ứng bên bảng A.

**Nó trả về tất cả những giá trị từ bảng bên phải + giá trị tương ứng từ bảng bên trái (hoặc null)**

Ví dụ về phép kết ngoại bên phải (cú pháp chuẩn ANSI 92):
 SELECT * 
 FROM employee 
 RIGHT OUTER JOIN 
 department 
  ON employee.DepartmentID = department.DepartmentID

Ví dụ về phép kết ngoại bên phải (cú pháp không chuẩn):
 SELECT *
 FROM employee, department
 WHERE employee.DepartmentID(+) = department.DepartmentID

 +-----------+--------------+----------------+--------------+
 | LastName | DepartmentID | DepartmentName | DepartmentID |
 +-----------+--------------+----------------+--------------+
 | Smith |  34 | Clerical  |  34 |
 | Jones |  33 | Engineering  |  33 |
 | Robinson |  34 | Clerical  |  34 |
 | Steinberg |  33 | Engineering  |  33 |
 | Rafferty |  31 | Sales   |  31 |
 | NULL |   NULL | Marketing |  35 |
 +-----------+--------------+----------------+--------------+

### Kết ngoại đủ

Một **phép kết ngoại đủ** (full outer join) kết hợp cả kết quả của cả phép kết ngoại bên trái và phép kết ngoại bên phải. Những phép kết này đưa ra bản ghi của cả hai bảng dữ liệu, và lấp đầy những dòng tương ứng bị thiếu của cả hai phía bằng NULLs.

Một vài hệ cơ sở dữ liệu không hỗ trợ chức năng này, nhưng nó có thể được thay thế bằng việc dùng phép kết ngoại bên trái và bên phải và phép hợp (union). (Xem dưới đây)

Ví dụ về phép kết ngoại đủ (cú pháp chuẩn ANSI 92):
 SELECT * 
 FROM employee 
 FULL OUTER JOIN 
 department 
  ON employee.DepartmentID = department.DepartmentID

 +-----------+--------------+----------------+--------------+
 | LastName | DepartmentID | DepartmentName | DepartmentID |
 +-----------+--------------+----------------+--------------+
 | Smith |  34 | Clerical  |  34 |
 | Jones |  33 | Engineering  |  33 |
 | Robinson |  34 | Clerical  |  34 |
 | Jasper  |  36 | NULL  |   NULL |
 | Steinberg |  33 | Engineering  |  33 |
 | Rafferty |  31 | Sales   |  31 |
 | NULL |   NULL | Marketing |  35 |
 +-----------+--------------+----------------+--------------+

Cùng 1 ví dụ, cho những cơ sở dữ liệu không hỗ trợ phép kết ngoại đủ:
 SELECT
 employee.LastName,
 employee.DepartmentID, 
 department.DepartmentName,
 department.DepartmentID
 FROM employee 
 LEFT JOIN 
 department 
  ON employee.DepartmentID = department.DepartmentID
 UNION
 SELECT
 employee.LastName,
 employee.DepartmentID, 
 department.DepartmentName,
 department.DepartmentID
 FROM employee
 RIGHT JOIN
 department
  ON employee.DepartmentID = department.DepartmentID
  WHERE employee.DepartmentID IS NULL

### Kết chéo

Mặc dù không được sử dụng thường xuyên, một **phép kết chéo** (cross join) là cơ sở mà dựa trên đó phép kết nội được tạo nên. Một phép kết chéo trả về tích Descartes của những tập hợp các dòng từ những bảng được kết.

Câu SQL dùng cho phép kết chéo liệt kê những bảng dữ liệu sẽ được kết (`[[From (SQL)|FROM]]`), nhưng không bao gồm bất cứ vị từ lọc nào (`[[Where (SQL)|WHERE]]`).

Ví dụ về phép kết chéo (cú pháp chuẩn ANSI 92):
 SELECT *
 FROM employee **CROSS JOIN** 
 department;

Ví dụ về phép kết chéo (cú pháp thay thế):
 SELECT *
 FROM employee, department;

 +-----------+---------------+----------------+--------------+
 | LastName | DepartmentID | DepartmentName | DepartmentID |
 +-----------+---------------+----------------+--------------+
 | Smith |  34 | Sales   |  31 |
 | Smith |  34 | Engineering  |  33 |
 | Smith |  34 | Clerical  |  34 |
 | Smith |  34 | Marketing |  35 |
 | Jones |  33 | Sales   |  31 |
 | Jones |  33 | Engineering  |  33 |
 | Jones |  33 | Clerical  |  34 |
 | Jones |  33 | Marketing |  35 |
 | Robinson |  34 | Sales   |  31 |
 | Robinson |  34 | Engineering  |  33 |
 | Robinson |  34 | Clerical  |  34 |
 | Robinson |  34 | Marketing |  35 |
 | Jasper  |  36 | Sales   |  31 |
 | Jasper  |  36 | Engineering  |  33 |
 | Jasper  |  36 | Clerical  |  34 |
 | Jasper  |  36 | Marketing |  35 |
 | Steinberg |  33 | Sales   |  31 |
 | Steinberg |  33 | Engineering  |  33 |
 | Steinberg |  33 | Clerical  |  34 |
 | Steinberg |  33 | Marketing |  35 |
 | Rafferty |  31 | Sales   |  31 |
 | Rafferty |  31 | Engineering  |  33 |
 | Rafferty |  31 | Clerical  |  34 |
 | Rafferty |  31 | Marketing |  35 |
 +-----------+---------------+----------------+--------------+

Ta có thể thấy phép kết chéo không tìm những bản ghi tương ứng. Phép kết này ít khi được dùng, ngoại trừ để phát sinh tất cả những kết hợp có khả năng của các bản ghi từ các bảng dữ liệu mà không chia sẻ phần tử chung.

Nếu A và B là 2 tập hợp thì phép kết chéo tạo ra |A| x |B| bản ghi ở kết quả (với |X| là lực lượng (số phần tử) của tập hợp X)

## Xem thêm

### Tổng quát
* Câu lệnh truy vấn - ''Select (SQL)''
* Câu lệnh thêm - ''Insert (SQL)''
* Câu lệnh cập nhật - ''Update (SQL)''

## Tham khảo