Sự khác nhau giữa LEFT JOIN và RIGHT JOIN

LEFT JOIN trong SQL là kiểu JOIN trả về tất cả các bản ghi từ bảng bên trái [bảng 1] và các bản ghi phù hợp từ bảng bên phải [bảng 2]. Nếu mệnh đề ON không khớp với bản ghi nào trong bảng bên phải thì LEFT JOIN sẽ vẫn trả về một hàng trong kết quả, nhưng giá trị là NULL trong mỗi cột từ bảng bên phải.

Điều này nghĩa là LEFT JOIN trả về tất cả giá trị từ bảng bên trái, cộng với các giá trị phù hợp từ bảng bên phải hoặc NULL trong trường hợp không có giá trị phù hợp nào.

  • Mệnh đề JOIN trong SQL

Cú pháp LEFT JOIN trong SQL

Cú pháp cơ bản của LEFT JOIN như sau:

SELECT cot1, cot2,... cotn
FROM bang1
LEFT JOIN bang2
ON bang1.cot_chung = bang2.cot_chung;

Tham số:

  • cot1, cot2,... cotn: tên các cột cần hiển thị ở kết quả truy vấn. Các cot được ngăn cách với nhau bằng dấu phẩy [,]
  • bang1, bang2: tên các bảng để lấy dữ liệu khi truy vấn.
  • cot_chung: thường là tên cột khóa ngoại tham chiếu từ bang1 đến cột định danh trong bang2 hoặc ngược lại.

Ví dụ về LEFT JOIN trong SQL

Giả sử hai bảng là NHANVIEN và TIENTHUONG có các bản ghi sau:

Bảng 1: NHANVIEN

+----+----------+-----+-----------+----------+ | ID | TEN |TUOI | DIACHI | LUONG | +----+----------+-----+-----------+----------+ | 1 | Thanh | 32 | Haiphong | 2000.00 | | 2 | Loan | 25 | Hanoi | 1500.00 | | 3 | Nga | 23 | Hanam | 2000.00 | | 4 | Manh | 25 | Hue | 6500.00 | | 5 | Huy | 27 | Hatinh | 8500.00 | | 6 | Cao | 22 | HCM | 4500.00 | | 7 | Lam | 24 | Hanoi | 10000.00 | +----+----------+-----+-----------+----------+

Bảng 2: TIENTHUONG

+-----+---------------------+-------------+--------+ |TT_ID| NGAY | NHANVIEN_ID | SOTIEN | +-----+---------------------+-------------+--------+ | 102 | 2019-01-08 00:00:00 | 3 | 3000 | | 100 | 2019-01-08 00:00:00 | 3 | 1500 | | 101 | 2019-02-20 00:00:00 | 2 | 1560 | | 103 | 2018-12-20 00:00:00 | 4 | 2060 | +-----+---------------------+-------------+--------+

Bây giờ, chúng ta hãy join hai bảng này bằng cách sử dụng LEFT JOIN như sau:

SQL> SELECT ID, TEN, SOTIEN, NGAY
FROM NHANVIEN
LEFT JOIN TIENTHUONG
ON NHANVIEN.ID = TIENTHUONG.NHANVIEN_ID;

Kết quả trả về là:

+----+----------+--------+---------------------+ | ID | TEN | SOTIEN | NGAY | +----+----------+--------+---------------------+ | 1 | Thanh | NULL | NULL | | 2 | Loan | 1560 | 2019-02-20 00:00:00 | | 3 | Nga | 3000 | 2019-01-08 00:00:00 | | 3 | Nga | 1500 | 2019-01-08 00:00:00 | | 4 | Manh | 2060 | 2018-12-20 00:00:00 | | 5 | Huy | NULL | NULL | | 6 | Cao | NULL | NULL | | 7 | Lam | NULL | NULL | +----+----------+--------+---------------------+

Xem thêm các kiểu JOIN:

  • INNER JOIN - Trả về các bản ghi có giá trị phù hợp giữa hai bảng.
  • RIGHT JOIN - Trả về tất cả các bản ghi từ bảng bên phải và các bản ghi phù hợp từ bảng bên trái.
  • FULL JOIN - Trả về tất cả bản ghi ở bảng trái và bảng phải kết hợp lại.
  • SELF JOIN - Kết hợp một bảng với chính nó như khi coi bảng đó là hai bảng, thay tên tạm thời ít nhất một bảng trong lệnh SQL.
  • CARTESIAN JOIN - Trả về tích Đề-các của các tập hợp bản ghi từ hai hoặc nhiều bảng đã được kết hợp.

An SQL JOIN clause is used to combine rows from two or more tables, based on a common field between them.

There are different types of joins available in SQL:

INNER JOIN: returns rows when there is a match in both tables.

LEFT JOIN: returns all rows from the left table, even if there are no matches in the right table.

RIGHT JOIN: returns all rows from the right table, even if there are no matches in the left table.

FULL JOIN: combines the results of both left and right outer joins.

The joined table will contain all records from both the tables and fill in NULLs for missing matches on either side.

SELF JOIN: joins a table to itself as if the table were two tables, temporarily renaming at least one table in the SQL statement.

CARTESIAN JOIN: returns the Cartesian product of the sets of records from the two or more joined tables.

We can take each first four joins in Details :

We have two tables with the following values.

TableA

id firstName lastName ....................................... 1 arun prasanth 2 ann antony 3 sruthy abc 6 new abc

TableB

id2 age Place ................ 1 24 kerala 2 24 usa 3 25 ekm 5 24 chennai

....................................................................

INNER JOIN

Note : gives the intersection of the two tables, i.e. rows TableA and TableB have in common.

Syntax

SELECT table1.column1, table2.column2... FROM table1 INNER JOIN table2 ON table1.common_field = table2.common_field;

Apply it in our sample table :

SELECT TableA.firstName,TableA.lastName,TableB.age,TableB.Place FROM TableA INNER JOIN TableB ON TableA.id = TableB.id2;

Result

firstName lastName age Place .............................................. arun prasanth 24 kerala ann antony 24 usa sruthy abc 25 ekm

LEFT JOIN

Note : gives all selected rows in TableA, plus any common selected rows in TableB.

Syntax

SELECT table1.column1, table2.column2... FROM table1 LEFT JOIN table2 ON table1.common_field = table2.common_field;

Apply it in our sample table :

SELECT TableA.firstName,TableA.lastName,TableB.age,TableB.Place FROM TableA LEFT JOIN TableB ON TableA.id = TableB.id2;

Result

firstName lastName age Place ............................................................................... arun prasanth 24 kerala ann antony 24 usa sruthy abc 25 ekm new abc NULL NULL

RIGHT JOIN

Note : gives all selected rows in TableB, plus any common selected rows in TableA.

Syntax

SELECT table1.column1, table2.column2... FROM table1 RIGHT JOIN table2 ON table1.common_field = table2.common_field;

Apply it in our sample table :

SELECT TableA.firstName,TableA.lastName,TableB.age,TableB.Place FROM TableA RIGHT JOIN TableB ON TableA.id = TableB.id2;

Result

firstName lastName age Place ............................................................................... arun prasanth 24 kerala ann antony 24 usa sruthy abc 25 ekm NULL NULL 24 chennai

FULL JOIN

Note : returns all selected values from both tables.

Syntax

SELECT table1.column1, table2.column2... FROM table1 FULL JOIN table2 ON table1.common_field = table2.common_field;

Apply it in our sample table :

SELECT TableA.firstName,TableA.lastName,TableB.age,TableB.Place FROM TableA FULL JOIN TableB ON TableA.id = TableB.id2;

Result

firstName lastName age Place ............................................................................... arun prasanth 24 kerala ann antony 24 usa sruthy abc 25 ekm new abc NULL NULL NULL NULL 24 chennai

Interesting Fact

  • For INNER joins the order doesn't matter.
  • For [LEFT, RIGHT or FULL] OUTER joins, the order matters.

Better to go check this Link it will give you interesting details about join order.

Lần đầu tiên nghe đến JOIN, mình đã nghĩ nó là cái gì đó cao siêu và hoành tráng lắm. Nhưng sau một hồi đọc rồi hiểu các kiểu về JOIN, thì mới nhận ra rằng nó chính là những truy vấn dữ liệu mà mình vẫn hay làm thường ngày, nhưng chỉ có điều là nó làm ngắn gọn và tăng tốc độ truy vấn lên thôi. "Chỉ có điều" ư? Không đâu! Làm ngắn gọn và tăng tốc độ truy vấn là giải quyết được một vấn đề khá to lớn đấy.

Join là gì?

Là một mệnh đề trong SQL, sử dụng để kết nối dữ liệu từ hai hay nhiều bảng trong cơ sở dữ liệu lại với nhau. Khi bạn cần truy vấn các cột dữ liệu từ nhiều bảng khác nhau để trả về trong cùng một tập kết quả, bạn cần dùng JOIN, SQL sẽ tạo ra một bảng tạm thời chứa dữ liệu kết quả từ JOIN.

Vai trò của join?

Ta xét ví dụ sau: Một mối quan hệ rất đơn giản giữa các thực thể trong một dự án Ruby on Rails:

class User has_many :books end class Book belongs_to :user end

Bây giờ, điều gì sẽ xảy ra khi cố gắng để có được user cho mỗi book?

books = Book.all user_names = books.map { |book| book.user.name }

Hãy nhìn vào console để xem điều gì xảy ra:

Book Load [0.7ms] SELECT "books".* FROM "books" User Load [0.2ms] SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load [0.1ms] SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] User Load [0.1ms] SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]

Dễ dàng thấy ra đây là vấn đề của N + 1 query. Truy vấn ban đầu của chúng ta [1 trong N +1] trả về collection có kích thước N, và đến lượt nó chạy một truy vấn cho mỗi một trong số chúng trong database [N trong N + 1].

Với việc sử dụng joins:

books = Book.all user_names = User.joins[:books]

và console:

Book Load [0.7ms] SELECT "books".* FROM "books" User Load [0.2ms] SELECT "users".* FROM "users" INNER JOIN "books" ON "users"."id" = "books"."id"

Vậy là joins trong trường hợp này đã giải quyết được vấn đề N+1 query. Đó mới chỉ là một trường hợp đơn giản ở trong Rails, ngoài ra join còn có giúp ích cho chúng ta rất nhiều nữa trong các truy vấn dữ liệu. Trước tiên, ta cần phải biết có những loại join nào.

Các kiểu join trong SQL?

Tùy vào các tình huống khác nhau với các yêu cầu về dữ liệu khác nhau, mà ta sẽ dùng các kiểu join khác nhau. Không có sự khác biệt nhiều giữa chúng, và cũng khá dễ hiểu, cơ bản có các loại như sau:

  1. INNER JOIN – trả về hàng khi có một sự phù hợp trong tất cả các bảng được join.

Ví dụ 1: Lấy ra những Orders của Customers, ta INNER JOIN 2 bảng Orders và Customers như sau:

SELECT Orders.OrderID, Customers.CustomerName, Orders.Orderdate FROM Orders INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID;

Trong đó bảng Orders là bảng trái vì bên trái từ khóa INNER JOIN, Customers là bảng phải. Biểu thức sau từ khóa ON cụ thể Orders.CustomerID = Customers.CustomerID là biểu thức khớp nối.

Ví dụ 2: Lấy ra các đơn hàng kèm theo tên khách hàng và tên người ship đơn hàng đó, ta INNER JOIN ba bảng Orders, Customers, Shippers:

SELECT Orders.OrderID, Customers.CustomerName, Shippers.ShipperName FROM [[Orders INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID] INNER JOIN Shippers ON Orders.ShipperID = Shippers.ShipperID];
  • LEFT JOIN trả về tất cả bản ghi bảng bên trái, ngay cả khi không có sự phù hợp trong bảng bên phải, còn những bản ghi nào của bảng bên phải phù hợp với bảng trái thì dữ liệu bản ghi đó được dùng để kết hợp với bản ghi bảng trái, nếu không có dữ liệu sẽ NULL.
SELECT Customers.CustomerName, Orders.OrderID FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID ORDER BY Customers.CustomerName;
  1. RIGHT JOIN – trả về tất cả các hàng từ bảng bên phải, ngay cả khi không có sự phù hợp nào ở bảng bên trái.
  • Trường hợp này hoạt động giống với LEFT JOIN theo chiều ngược lại.
  1. FULL JOIN – trả về hàng khi có một sự phù hợp trong một trong các bảng.
  • Xét tất cả các kết quả, với SQLite không hỗ trợ [có thể thay thế bằng LEFT JOIN kết hợp với UNION]
SELECT Customers.CustomerName, Orders.OrderID FROM Customers FULL OUTER JOIN Orders ON Customers.CustomerID = Orders.CustomerID ORDER BY Customers.CustomerName;
  1. SELF JOIN – được sử dụng để tham gia một bảng với chính nó như thể bảng đó là hai bảng, tạm thời đổi tên ít nhất một bảng trong câu lệnh SQL.

Nếu muốn đọc để hiểu rõ hơn, các bạn có thể qua đọc series các bài viết về join sql trên w3chool: //www.w3schools.com/sql/sql_join.asp

Ví dụ về join trong Rails app

Client.joins['LEFT OUTER JOIN addresses ON addresses.client_id = clients.id'] SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id
  1. Ví dụ join nhiều bảng cùng lúc
Category.joins[:posts => [{:comments => :guest}, :tags]] SELECT categories.* FROM categories INNER JOIN posts ON posts.category_id = categories.id INNER JOIN comments ON comments.post_id = posts.id INNER JOIN guests ON guests.comment_id = comments.id INNER JOIN tags ON tags.post_id = posts.id
  1. Ví dụ lấy ra những User cùng với bookmarks post của user đó:
User.joins["LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id"] => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id

Trong framework Ruby on Rails cung cấp những method query preload, eager_load, includes, references, joins, mỗi một phương pháp có cách hoạt động phù hợp với từng mục đích khác nhau. Việc không nắm rõ cách thức hoạt động, ưu điểm, nhược điểm của những method đó sẽ dẫn đến việc sử dụng sai lầm, tốn tài nguyên cũng như vấn đề performance của ứng dụng. Để hiểu rõ hơn các method trên và để biết được cách thức hoạt động và tốc độ xử lý của chúng, các bạn hãy tham khảo bảo viết: Tìm hiểu preload, eager_load, includes, references, and joins in Rails

Còn với vấn để mình đưa ra ở ví dụ đầu, khi sử dụng với preload, eager_load và includes thì nó sẽ như thế này đây:

books = Book.all user_names = books.preload[:user].map { |book| book.user.name } Book Load [0.3ms] SELECT “books”.* FROM “books” User Load [0.4ms] SELECT “users”.* FROM “users” WHERE “users”.”id” IN [1, 2, 3] user_names = books.eager_load[:user].map { |book| book.user.name } #=> SQL [0.4ms] SELECT “books”.”id” AS t0_r0, “books”.”title” AS t0_r1, “books”.”author” AS t0_r2, #=> “books”.”books_id” AS t0_r3, “books”.”user_id” AS t0_r4, “books”.”created_at” AS t0_r5, #=> “books”.”updated_at” AS t0_r6, “users”.”id” AS t1_r0, “users”.”name” AS t1_r1, #=> “users”.”created_at” AS t1_r2, “users”.”updated_at” AS t1_r3 FROM “books” #=> LEFT OUTER JOIN “users” ON “users”.”id” = “books”.”user_id” books.includes[:user].where['users.name="Guava"'] #=> #=> SELECT "books".”id” AS t0_r0, "books"."title" AS t0_r1, #=> "books."author" AS t0_r2, "books"."books_id" AS t0_r3, #=> "books"."user_id" AS t0_r4, "books"."created_at" AS t0_r5, #=> "books"."updated_at" AS t0_r6, "users"."id" AS t1_r0, #=> "users"."name" AS t1_r1, "users"."created_at" AS t1_r2, #=> "users"."updated_at" AS t1_r3 FROM "books" #=> LEFT OUTER JOIN "users" ON "users"."id"= "books"."user_id" #=> WHERE [users.name="Guava"]

Tham khảo

//www.w3schools.com/sql/sql_join.asp

//guides.rubyonrails.org/active_record_querying.html#joining-tables

//viblo.asia/p/join-hay-khong-join-mot-hanh-dong-includes-bWrZnNwwZxw

//viblo.asia/p/tim-hieu-preload-eager-load-includes-references-and-joins-in-rails-roavrwPXGRM

Video liên quan

Chủ Đề