# SUBQUERY (truy vấn lồng) ## Giới thiệu :::info :bulb: **Định nghĩa**: truy vấn lồng là câu truy vấn mà bên trong của nó chưa 1 câu truy vấn khác ::: ```sql SELECT A FROM X WHERE ... (SELECT B FROM Y WHERE ...) ... ``` ## Vị trí Nằm đâu cũng được từ **SELECT**, **FROM** cho tới **WHERE** (thường nhất) vd nằm trong select ```sql select ProductID, Name, (SELECT COUNT(*) FROM Production.ProductReview PR WHERE P.ProductID = PR.ProductID) AS review_amount from Production.Product P ORDER BY review_amount desc ``` nằm trong from ```sql select PR.ProductID, P.Name, PR.Comments, PR.ReviewerName from (SELECT ProductID, Comments, ReviewerName FROM Production.ProductReview ) AS PR left join Production.Product P on PR.ProductID = P.ProductID ``` nằm trong where ```sql select * from HumanResources.EmployeePayHistory where rate > (select rate from HumanResources.EmployeePayHistory where BusinessEntityID = 250 and year(RateChangeDate) =2012 ) ``` ## Phân loại > Ta có 2 loại truy vấn lồng > 1. Truy vấn lồng phân cấp. Câu con **độc lập** câu cha > 2. Truy vấn lồng tương quan. Câu con **phụ thuộc** câu cha ### Phân cấp ```sql SELECT A FROM X WHERE…(SELECT B,C FROM Y ) … ``` 2 câu query độc lập nhau Trong mệnh đề phân cấp ta sẽ làm quen với các toán tử > IN > ALL > ANY | SOME ### Tương quan ```sql SELECT A FROM X WHERE…(SELECT B,C FROM Y WHERE B = X.A) … ``` ta thấy mệnh đề WHERE của câu con có liên quan đến câu cha Trong mệnh đề tương quan ta sẽ làm quen với các toán tử > EXISTS > ALL > ANY | SOME ### EXISTS cú pháp ```sql= EXISTS ( subquery ) ``` :::info :bulb: **Định nghĩa**: Toán tử EXISTS trả về **True** nếu truy vấn con trả về một hoặc nhiều dòng dữ liệu. ::: ví dụ Lấy ra tên họ của những người xuất hiện trong bảng Employee có mức lương rate cao hơn 30 ```sql= select LastName, FirstName from Person.Person P where exists (select 1 from HumanResources.EmployeePayHistory EP where P.BusinessEntityID = EP.BusinessEntityID and EP.Rate > 30) ; ``` trái với EXIST ta có NOT EXIST ```sql= select LastName, FirstName from Person.Person P where not exists (select 1 from HumanResources.EmployeePayHistory EP where P.BusinessEntityID = EP.BusinessEntityID and EP.Rate < 30) ; ``` > Ưu điểm của việc sử dụng toán tử SQL EXISTS và NOT EXISTS là việc thực thi truy vấn con bên trong có thể dừng lại miễn là tìm thấy một bản ghi phù hợp. >Nếu truy vấn con yêu cầu quét một khối lượng lớn các bản ghi, việc dừng thực thi truy vấn con ngay sau khi một bản ghi được khớp có thể tăng tốc đáng kể thời gian phản hồi truy vấn tổng thể ### IN ```sql= columns [ NOT ] IN ( subquery | expression [ ,...n ] ) ``` :::info :bulb: **Định nghĩa**: Toán tử IN trả về **True** nếu giá trị khớp với 1 hay nhiều giá trị trong kết quả trả về hay danh sách ::: ví dụ Lấy ra tên họ của những người xuất hiện trong bảng Employee có mức lương rate cao hơn 30 ```sql= select LastName, FirstName from Person.Person P where P.BusinessEntityID in (select EP.BusinessEntityID from HumanResources.EmployeePayHistory EP where EP.Rate > 30) ; ``` Phủ đinh của IN là NOT IN :::info :bulb: **Định nghĩa**: Nếu truy vấn con hoặc biểu thức được so sánh với <columns> có NULL trong kết quả sử dụng IN hoặc NOT IN đều trả về UNKNOWN. Sử dụng các giá trị null cùng với IN hoặc NOT IN có thể tạo ra kết quả không mong muốn. ::: vd ```sql= select 'true' where 3 in (1, 2, 3, null) select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null select 'true' where 3 not in (1, 2, null) select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null ``` ```sql select 'true' where 3 in (1, 2, 3, null) select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null 3 = 1 or 3 = 2 or 3 = 3 or 3 = null which is: FALSE or FALSE or TRUE or UNKNOWN which evaluates to TRUE ``` ```sql select 'true' where 3 not in (1, 2, null) select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null 3 <> 1 and 3 <> 2 and 3 <> null which evaluates to: TRUE and TRUE and UNKNOWN which evaluates to: UNKNOWN ``` ### ALL ```sql= scalar_expression ("<", ">", "<>", "!=", "=") ALL [subquery] ``` trả về kết quả đúng nếu scalar_expression lớn hơn tất cả hay khác tất cả ,... các kết quả cảu sub query ví dụ tìm họ tên người có rate lương cao nhất ```sql= select LastName, FirstName from HumanResources.EmployeePayHistory ER inner join Person.Person P on ER.BusinessEntityID = P.BusinessEntityID where ER.Rate >= ALL (select Rate from HumanResources.EmployeePayHistory ) ``` ### ANY | SOME ```sql= scalar_expression ("<", ">", "<>", "!=", "=") any|some [subquery] ``` trả về kết quả đúng nếu scalar_expression lớn hơn Một hay một vài hay khác Một hay một vài ,... các kết quả cảu sub query # Một vài Ví dụ 1 Tìm ra SalesOrderID, CustomerID mua được nhiều sản phẩm nhất trong 1 đơn hàng VỚI IN ```SQL SELECT H.SalesOrderID, H.CustomerID FROM Sales.SalesOrderHeader H WHERE H.SalesOrderID in ( select top 1 with ties D.SalesOrderID from Sales.SalesOrderDetail D group by D.SalesOrderID order by SUM(D.OrderQty) desc ) ``` VỚI EXITST ```sql= SELECT H.SalesOrderID, H.CustomerID FROM Sales.SalesOrderHeader H WHERE exists ( select 1 from Sales.SalesOrderDetail D1 where D1.SalesOrderID = H.SalesOrderID group by D1.SalesOrderID having SUM(D1.OrderQty) >= ALL( SELECT SUM(D2.OrderQty) FROM Sales.SalesOrderDetail D2 GROUP BY D2.SalesOrderID) ) ``` 2. Tìm ra tất cả thông tin về người của những nhân viên Sales ```sql= SELECT P.* FROM Person.Person p WHERE P.BusinessEntityID IN (SELECT S.BusinessEntityID FROM Sales.SalesPerson S) ``` cách 2 có the giải bằng join cách 3 có thể giải bằng EXISTS ```sql= SELECT P.* FROM Person.Person P WHERE EXISTS (SELECT S.BusinessEntityID FROM Sales.SalesPerson S WHERE S.BusinessEntityID = P.BusinessEntityID) ``` # Bài tập 1. ![](https://i.imgur.com/XwTld5R.png) Dựa vào bảng trên lấy ra BusinessEntityID,LoginID,JobTitle, VacationHours. Với điều kiện VacationHours >= số giờ nghỉ phép trung bình [avg(VacationHours)] của toàn bảng. 2.![](https://i.imgur.com/jspqIB0.png) lấy tên của những nhân viên cũng đồng thời là khách hàng 3. ![](https://i.imgur.com/y2pnHoc.png) giá bằng giá sản phẩm tên 'Touring End Caps' 4. Cùng quan sát NOT IN nhạy cảm thể nào với giá trị NULL. ![](https://i.imgur.com/zSlbeAW.png) bảng vendor là bảng chưa thông tin những Công Ty mà công ty Adventure work mua linh kiện. Product vendor là bảng mapping với bảng Vendor thông qua những sản phẩm mà công ty đó bán. Bước 1 ta tạo ra 1 bảng MyVendor với câu lệnh sau copy chạy ```sql CREATE TABLE Purchasing.MyVendors ( BusinessEntityID int, AccountNumber nvarchar(15), Name nvarchar(50) ) GO -- chọn vài dòng dữ liệu từ bảng vendor bỏ vô MyVendors INSERT INTO Purchasing.MyVendors SELECT BusinessEntityID, AccountNumber, Name FROM Purchasing.Vendor WHERE BusinessEntityID IN (SELECT BusinessEntityID FROM Purchasing.ProductVendor) AND BusinessEntityID like '14%' ``` Dùng "NOT IN" để tìm ra "**Product.[Name]**", "**ProductVendor.BusinessEntityID**", "**ProductVendor.StandardPrice**", "**Product.ListPrice**". Với những **ProductVendor.BusinessEntityID** không xuất hiện trong bảng **MyVendors** vừa tạo _ Sau khi chạy xong ra kết quả như bên dưới ![](https://i.imgur.com/sJ0rsJ4.png) ta tiếp tục thêm một câu lệnh thêm 1 dòng dữ liệu Null vào bang **MyVendors** ```sql= INSERT INTO Purchasing.MyVendors VALUES (NULL, '123', 'TEST_NULL') ``` Sau đó chạy lại câu query của bạn lucs nảy, và so sánh dữ liệu. 5. Tìm cách giải quyết kết quả sai câu 4. _ gợi ý > 1 đảm bảo câu subquery không chứa giá trị NULL > 2 Hoặc đảm bảo bẳng MyVendors không thêm đc giá trị NULL