Cẩn Thận Với Join Bất Cân Bằng

Vũ Huy Tâm

Thông thường khi viết lệnh JOIN, trong biểu thức ở mệnh đề join bạn dùng toán tử “=”. Kiểu join này tạm gọi là join cân bằng, còn kiểu join tạm gọi là bất cân bằng là khi biểu thức ở mệnh đề join dùng các toán tử bất cân bằng như >, <, !=...

-- Join cân bằng
SELECT A.*
FROM Table_A A JOIN Table_B B ON A.ID = B.ID
 
--Join bất cân bằng
SELECT A.*
FROM Table_A A JOIN Table_B B ON A.ID != B.ID

Trong ví dụ trên bạn có nghĩ rằng toán tử “!=” sẽ lấy ra các bản ghi khác nhau giữa bảng A và bảng B, hay nói cách khác là các bản ghi trong A mà không có trong B (có lý vì “!=” tác dụng ngược lại với “=”)? Nếu bạn nghĩ như vậy thì bạn đã nhầm to, và kết quả là rất tai hại. Hãy xem qua đoạn code dưới đây:

--Tạo bảng A có 5 bản ghi
CREATE TABLE A(ID INT, c VARCHAR(20))
INSERT INTO A SELECT 1,'a'
INSERT INTO A SELECT 2,'b'
INSERT INTO A SELECT 3,'c'
INSERT INTO A SELECT 4,'d'
INSERT INTO A SELECT 5,'e'
 
--Tạo bảng B với 3 bản ghi
CREATE TABLE B(ID INT)
INSERT INTO B SELECT 2
INSERT INTO B SELECT 3
INSERT INTO B SELECT 6
 
--Join hai bảng
SELECT A.*
FROM A
JOIN B ON A.ID != B.ID
 
--Kết quả
ID	c
--------
1	a
3	c
4	d
5	e
1	a
2	b
4	d
5	e
1	a
2	b
3	c
4	d
5	e

Như vậy kết quả không phải là các bản ghi trong-A-không-trong-B, vốn chỉ có 1, 4, và 5, mà còn lủng củng bao nhiêu thứ khác nữa. Rõ ràng là nó không làm như ý bạn mong đợi, thế thì nó làm gì? Phép join về bản chất là để ghép nối các bản ghi thỏa mãn điều kiện join với nhau. Khi hai bảng đã được đặt cạnh nhau điều kiện join được đem ra xem xét. Các bản ghi thỏa mãn điều kiện join được lấy ra, các bản ghi không thỏa mãn bị loại bỏ, điều này luôn là như vậy cho dù là join cân bằng hay bất cân bằng. Trong ví dụ trên, mỗi bản ghi của bảng A được đem ra đối chiếu với từng bản ghi của bảng B, nếu điều kiện join thỏa mãn (A.ID != B.ID) thì bản ghi A được lấy ra, nếu không thỏa mãn (A.ID = B.ID) thì bị bỏ đi. Hãy xét bản ghi đầu tiên của bảng A, ID = 1, nó thỏa mãn với cả ba bản ghi của bảng B (vì cả ba ID của B đều khác 1), vì thế nó được lấy ra ba lần. Bản ghi thứ hai ID = 2 thỏa mãn với hai bản ghi từ bảng B (ID = 3 và 6), cho nên nó vẫn xuất hiện hai lần. Bạn hãy sửa lại lệnh SELECT trên để thêm cả cột ID từ bảng B vào và sẽ thấy rõ:

SELECT A.ID AS ID_A, A.c, B.ID AS ID_B
FROM A
JOIN B ON A.ID != B.ID
 
--Kết quả
ID_A	c	ID_B
------------------
1	a	2
3	c	2
4	d	2
5	e	2
1	a	3
2	b	3
4	d	3
5	e	3
1	a	6
2	b	6
3	c	6
4	d	6
5	e	6

Câu lệnh join đã làm đúng phận sự của nó: lấy hết các bản ghi trong đó ID_A khác với ID_B. Vậy là join bất cân bằng không thể lấy ra các bản ghi khác nhau giữa hai bảng như left join, mặc dù trông nó có vẻ như là vậy. Trong trường hợp muốn lấy các bản ghi khác nhau bạn cần dùng LEFT JOIN (cân bằng) hoặc NOT EXISTS:

--LEFT JOIN
SELECT A.*
FROM A
LEFT JOIN B ON A.ID = B.ID
WHERE B.ID IS NULL
 
--NOT EXISTS
SELECT A.*
FROM A
WHERE NOT EXISTS(SELECT 1 FROM B WHERE A.ID = B.ID)

Vậy join bất cân bằng dùng trong trường hợp nào? Có lẽ phổ biến nhất là khi bạn cần self join (join bảng với chính nó). Ví dụ với bảng A ở trên, tìm các cặp giá trị cột c mà ID của anh này lớn hơn ID của anh kia:

SELECT A1.c, A2.c
FROM A AS A1
JOIN A AS A2 ON A1.ID > A2.ID

Bạn còn dùng join bất cân bằng trong trường hợp nào nữa?




Tags:

2 Comments
Posted on 10/9/2014 | Categories: SQL Server Programming

Các bài viết tương tự

Comments
  • Red Devilic (12/09/2014 2:07 am)

    Yes, anh Tâm post bài này làm em nhớ lại một vài trường hợp dùng non-equal join. Em sẽ tổng hợp lại thành một bài :D

  • tung (22/09/2014 4:14 am)

    Thank bác tâm , kiến thức thật là vô hạn :)

Leave a Reply

Hướng dẫn: Để nhập mã T-SQL bạn dùng thẻ <pre lang="tsql"> và </pre>.
Ví dụ: <pre lang="tsql">SELECT * FROM MyTable</pre>