# JOIN IN T-SQL * inner join * full join * left join * right join * cross join Data mẫu để minh họa Production | product_id | name | price | | ---------- | --------- | ----- | | p1 | cpu | 100 | | p2 | ram | 20 | | p3 | fan | 10 | | p4 | mainboard | 80 | Transaction | transaction_id | product_id | amount | | -------------- | ---------- | ------ | | t1 | p1 | 1 | | t2 | p2 | 2 | | t3 | p3 | 3 | | t4 | p1 | 4 | | t5 | p5 | 5 | ## Inner join syntax ```sql= SELECT column_name(s) FROM <table1> tb1 INNER JOIN <table2> tb2 on tb1.<columns1> = tb2.<columns2> ``` ![](https://i.imgur.com/xcH1rHO.png) :::info :bulb: **Hint**: Dùng để chọn ra những **dòng** có điểm chung dựa vào **điều kiện join** của 2 bảng ::: ví dụ ```sql= SELECT * FROM Transaction T inner join Production P on T.product_id = P.product_id ``` ![](https://i.imgur.com/FFC5vZ8.png) > Thông thường trong Sql server người ta chỉ viết **JOIN** là **INNER JOIN** ## Left join syntax ```sql= SELECT column_name(s) FROM <table1> tb1 LEFT JOIN <table2> tb2 on tb1.<columns1> = tb2.<columns2> ``` ![](https://i.imgur.com/jf4vI0O.png) :::info :bulb: **Hint**: dùng để lấy thêm thông tin từ bảng 2 bổ sung thêm cho bảng 1. Số lượng record của bảng 1 sẽ được giữ nguyên hoặc nhiều hơn. ::: ví dụ ```sql= SELECT * FROM Transaction T left join Production P on T.product_id = P.product_id ``` ![](https://i.imgur.com/6yd4QEF.png) **** ## Right join syntax ```sql= SELECT column_name(s) FROM <table1> tb1 RIGHT JOIN <table2> tb2 on tb1.<columns1> = tb2.<columns2> ``` :::info :bulb: **Hint**: Giống như left join nhưng lấy bảng bên phải làm chuẩn. Số lượng record của bảng 2 sẽ được giữ nguyên hoặc nhiều hơn. ::: ví dụ ```sql= SELECT * FROM Transaction T right join Production P on T.product_id = P.product_id ``` ![](https://i.imgur.com/UwwsmsU.png) **** ## full join syntax ```sql= SELECT <columns> from <table1> tb1 FULL JOIN <table2> tb2 on tb1.<columns1> = tb2.<columns2> ``` :::info :bulb: **Hint**: Lấy hết thông tin ở cả 2 bảng nếu như dòng nào không thỏa điều kiện join thì có giá trị NULL ::: ví dụ ```sql= SELECT * FROM Transaction T full join Production P on T.product_id = P.product_id ``` ![](https://i.imgur.com/fJ5MGps.png) ## CROSS join syntax ```sql= SELECT column_name(s) FROM <table1> tb1 CROSS JOIN <table2> tb2 ``` :::info :bulb: **Hint**: LẤY 2 TABLE NHÂN LẠI VỚI NHAU ::: ví dụ ```sql= SELECT * FROM Transaction T cross join Production P ``` ![](https://i.imgur.com/v8AfmBb.png) ## MỘT SỐ TIP ### 1 ![](https://i.stack.imgur.com/qje6o.png) :::info :bulb: **Chú ý**: Việc quan trong khi join 2 table là xác định "Điều kiện join". Nếu mối quan hệ 1-n thì phải chọn cột PK join với cột FK. Nếu không dữ liệu sẽ bị duplicate ::: ### 2 multi Join Join nhiều bảng lại với nhau, ta lấy ví dụ có database ![](https://i.imgur.com/sI6XwhP.png) ta cần thông tin **customer_name , product_name, amount, price** ```sql= SELECT C.customer_name ,P.name as product_name ,T.amount p.price FROM Transaction T inner join Customer C on T.customer_id = C.customer_id left join Production P on T.product_id = P.product_id ``` Kết quả ![](https://i.imgur.com/mWDGjWI.png) ta cùng đi phân tích tại sao kết quả được như vậy. Trong câu query trên ta thấy có 2 lệnh join > 1. là Inner Join > 2. là Left Join SQL server sẽ chạy lệnh đầu tiên ```sql= Transaction T inner join Customer C on T.customer_id = C.customer_id ``` ra kết quả ![](https://i.imgur.com/ZgYAhdv.png) sau đó đem kết quả vừa ra đem join tiếp với câu lệnh ```sql= left join Production P on T.product_id = P.product_id ``` ta được kết quả ![](https://i.imgur.com/bB6Wy63.png) Sau đó SQL server chọn ra các cột ở mệnh đề **Select** :::info :bulb: **Chú ý**: Nếu ở mệnh đề join 2 mà ta dùng **INNER JOIN** thì kết quả cuối cùng sẽ bị mất đi 1 dòng khi đấy nếu ta tính tổng cột **amount** hay **price** sẽ không còn đúng. Nên cân nhắc trước khi lựa phép JOIN ::: # UNION, EXCEPT, INTERSECT, SUBQUERY :::info :bulb: **NOTE**: Nếu ở mệnh đề **JOIN** là ta ghép bảng theo **chiều ngang** Thì **UNION,EXCEPT INTERSECT** là các mệnh để cho phép ta ghép bảng theo **chiều dọc** ::: ## UNION > UNION là phép ́hội (có thể hiểu như phép cộng) có 2 loại > 1. UNION : sẽ loại đi các dòng trùng lặp > 2. UNION ALL : lấy tất cả các dòng giữa 2 bảng ```sql= SELECT tb1.column_name1, tb1.column_name2,... FROM <table1> tb1 UNION (UNION ALL) SELECT tb2.column_name1, tb2.column_name2,... FROM <table2> tb2 ``` Ví dụ ![](https://i.imgur.com/UxPV4dn.png) chạy thử ```sql= select TransactionID AS [transaction_1] , ProductID as [product_1] from Production.TransactionHistory UNION select TransactionID AS [transaction_2] , ProductID as [product_2] from Production.TransactionHistoryArchive ``` ![](https://i.imgur.com/yh2nQJo.png) :::info :bulb: **chú ý**: Khi Union **số lượng** cột và **thứ tự** các cột giữa 2 bảng phải giống nhau Tên của bảng kết quả sẽ lấy theo bảng đầu tiên ::: ## EXCEPT > EXCEPT là phép ́trừ ```sql= SELECT tb1.column_name1, tb1.column_name2,... FROM <table1> tb1 EXCEPT SELECT tb2.column_name1, tb2.column_name2,... FROM <table2> tb2 ``` Ví dụ ![](https://i.imgur.com/vKBpOQC.png) lấy những dòng có trong table1 mà không có trong table2 chạy thử ```sql= SELECT ProductID FROM Production.Product EXCEPT SELECT ProductID FROM Production.WorkOrder ; ``` :::info :bulb: **chú ý**: EXCEPT giống Union về **số lượng** cột và **thứ tự** các cột giữa 2 bảng phải giống nhau Tên của bảng kết quả sẽ lấy theo bảng đầu tiên ::: ## INERSECT > INERSECT là phép giao ```sql= SELECT tb1.column_name1, tb1.column_name2,... FROM <table1> tb1 INERSECT SELECT tb2.column_name1, tb2.column_name2,... FROM <table2> tb2 ``` Ví dụ ![](https://i.imgur.com/Ul5JW6g.png) chạy thử ```sql= SELECT ProductID FROM Production.Product INTERSECT SELECT ProductID FROM Production.WorkOrder ; ``` :::info :bulb: **chú ý**: INERSECT giống Union về **số lượng** cột và **thứ tự** các cột giữa 2 bảng phải giống nhau Tên của bảng kết quả sẽ lấy theo bảng đầu tiên ::: # Bài tập ## join Data: Sử dụng bộ dữ liệu Adventure Work 2017 OLTP [digaram](https://i0.wp.com/improveandrepeat.com/wp-content/uploads/2018/12/AdvWorksOLTPSchemaVisio.png?ssl=1) [Dictionary](https://www.sqldatadictionary.com/AdventureWorks2014/) 1. Dựa vào 2 bảng Sales.Customer và Sales.SalesOrderHeader. Hãy lấy ra Customer_ID và Tổng số lượng đơn hàng của họ sắp xếp kết quả theo số lượng đơn hàng giảm dần. ![](https://i.imgur.com/bGifBOv.png) ::: success gợi ý Sales.Customer(PersonID) và Person.person(BusinessEntityID) quan hệ với nhau ::: --------------------------------------------------------- 2. ![](https://i.imgur.com/2Aiszr7.png) Dựa vào diagram này hãy cho biết số lượng 20 SalesOrderID có số tiền giảm giá nhiều nhất, lấy ra thông tin SalesOderID, Số lượng sản phẩm được giảm giá, Số tiền được giảm --------------------------------------------------------- 3. ![](https://i.imgur.com/mf0hoYx.png) Bảng ProductListPriceHistory là bảng lưu thông tin lịch sử các lần thay đổi giá bán của sản phẩm. VD sản phẩm A qua 3 lần thay đổi giá sẽ được lưu như sau: | ProductID | StartDate | EndDate | ListPrice | | --------- | --------- | --------- | --------- | | A | 1.4.2022 | 31.5.2022 | 100 | | A | 1.6.2022 | 31.8.2022 | 80 | | A | 1.9.2022 | NULL | 60 | Giá mới nhất sẽ có EndDate = NULL. Và cột **ListPrice** ở bảng **Product** luôn bằng giá mới nhất của sản phẩm Dựa vào 2 bảng này lấy ra 10 tên sản phẩm có sự chênh lệch giá nhiều nhất từ quá khứ. Query ra 2 cột tên sản phẩm và số tiền giảm giá --------------------------------------------------------- 4. Dùng lại 2 table câu 3 hãy cho biết top 10 sản phẩm có **SỐ LẦN** thay đổi giá nhiều nhất --------------------------------------------------------- 5. Lấy ra danh sách customer và product chưa bao giờ mua hàng hay bán. ::: success Gợi ý: Sẽ có các khách hàng không bao giờ mua hàng cũng như có những sản phẩm chưa giờ bán được. Ta query ra 2 cột Customer_Id va Product_id. Nếu cột Customer_Id có giá trị thì Product_id NUll và ngược lại. dựa vào 4 bảng: > [Sales.SalesOrderHeader] [Sales.SalesOrderDetail] [Production.Product] [Sales.Customer] ::: --------------------------------------------------------- 6. ![](https://i.imgur.com/jWQRutI.png) Lấy ra danh sách các nhân viên (employee) của công ty, bao gồm: BusinessEntityID, Title, FirstName, MiddleName, LastName, EmailAddress. --------------------------------------------------------- 7. ![](https://i.imgur.com/Aq8pePK.png) Lấy ra các đầu mối liên lạc của các đối tác của công ty (vendor contact), bao gồm: BusinessEntityID, Title, FirstName, MiddleName, LastName, Position (cột Name ở bảng Person.ContactType), PhoneNumber, Name (phone number type), ## union 1. ![](https://i.imgur.com/I0mhSlK.png) Ta có 2 bảng <li> <b>[Production].[TransactionHistoryArchive]</b> chứa thông tin các giao dịch từ năm 16/4/2011 đến 30/07/2013 </li> <li> <b>[Production].[TransactionHistory]</b> chứa thông tin các giao dịch từ năm 31/07/2013 đến 03/08/2014 </li> Hãy viết câu query lấy đầy đủ các cột của cả 2 bảng. Gộp 2 bảng này lại thành 1 bảng lưu Transaction từ 16/4/2011 đến 03/08/2014. 2. ![](https://i.imgur.com/CnyX152.png) Ta có 2 bảng <li> <b>[Production].[ProductListPriceHistory]</b> chứa thông tin lịch sử thay đổi của giá bán từng sản phẩm </li> <li> <b>[Production].[ProductCostHistory]</b> chứa thông tin lịch sử chi phí nhập hàng của sản phẩm </li> Hãy viết câu query lấy đầy đủ các cột của 2 bảng. Gộp 2 bảng thành 1 bảng và tạo thêm 1 cột <b>Type</b> chứa 2 giá trị <i>'cost'</i> hoặc <i>'price'</i> để phân biệt giữa dòng nào là chi phí sản xuất, và giá thành bán ra. Gợi ý cú pháp thêm 1 cột: ![](https://i.imgur.com/XzEFjv6.png)