Най-четените учебни материали
Най-новите учебни материали
***ДОСТЪП ДО САЙТА***
ДО МОМЕНТА НИ ПОСЕТИХА НАД 2 500 000 ПОТРЕБИТЕЛИ
БЕЗПЛАТНИТЕ УЧЕБНИ МАТЕРИАЛИ ПРИ НАС СА НАД 7 700
Ако сме Ви били полезни, моля да изпратите SMS с текст STG на номер 1092. Цената на SMS е 2,40 лв. с ДДС.
Вашият СМС ще допринесе за обогатяване съдържанието на сайта.
SMS Login
За да използвате ПЪЛНОТО съдържание на сайта изпратете SMS с текст STG на номер 1092 (обща стойност 2.40лв.)Съставяне на подзаявки |
Съставяне на подзаявки SQL предоставя възможност за влагане на заявки една в друга. Например: 1) Заявка, извеждаща данните за продуктите с минимална доставна цена. SELECT * FROM Products WHERE price = ( SELECT MIN(price) FROM Products ); 2) Заявка, извеждаща данните за продуктите с доставна цена, по-голяма от средната доставна цена на всички продукти. SELECT * FROM Products WHERE price > ( SELECT AVG(price) FROM Products ); В тези примери трябва подзаявката да връща една стойност – един ред и една колона, в противен случай се получава съобщение за грешка. За да се осигури връщане на точно една стойност, се използват обобщаващи функции или условие за стойността на колоната (колоните) на първичния ключ или на колона (колони) с ограничение за уникалност. Когато подзаявката не върне резултат, конструкцията няма да върне грешка, но и основната заявка няма да върне резултат. Разширение на стандартния SQL позволява извеждане на резултата от подзаявката в списъка с полета на главната заявка. Например: 3) Заявка, извеждаща име на продукт и брой на продажбите на съответния продукт. SELECT ProductName, ( SELECT COUNT(*) FROM SaleDetails sd WHERE sd.ProductID = p.ProductID ) AS CountOfProductsSales FROM Products p; Тази заявка е еквивалентна на следната: SELECT ProductName, COUNT(sd.ProductID) AS CountOfProductsSales FROM Products p LEFT JOIN SaleDetails sd ON sd.ProductID = p.ProductID GROUP BY ProductName; За подзаявки, генериращи произволен брой редове, се използва специалния оператор IN. Например: 4) Заявка, извеждаща данните за продажбите на служителите с дадена фамилия. SELECT * FROM Sales WHERE EmployeeID IN ( SELECT EmployeeID FROM Employees WHERE LastName = "Име" ); Тази заявка е еквивалентна на следната: SELECT s.* FROM Sales s INNER JOIN Employees e ON e.EmployeeID = s.EmployeeID WHERE LastName = "Име"; Едно съединение може да се напише като подзаявка, но обратното не винаги е вярно. Съединението в повечето случаи се изпълнява по-бързо, отколкото еквивалентната подзаявка, но целта на заявката е по-ясно определена, когато даден въпрос бъде решен чрез подзаявка. 5) Заявка, извеждаща данните за клиентите, намиращи се в градове, в които има доставчици. SELECT * FROM Customers WHERE City IN ( SELECT DISTINCT City FROM Suppliers ); 6) Заявка, извеждаща идентификаторите и имената на продуктите, които не са продавани през изминалия месец и продажбата им не е преустановена. SELECT ProductID, ProductName FROM Products WHERE ProductID NOT IN ( SELECT DISTINCT ProductID FROM SaleDetails sd INNER JOIN Sales s ON sd.SaleID = s.SaleID WHERE SaleDate BETWEEN DATEADD("m", -1, Date()) AND Date() ) AND Discontinued = 0; 7) За извеждане на първите три най-високи цени и съответните имена на продукти, ще е необходимо да се използва DISTINCT с TOP. Ако едновременно с това бъде избрано и името на съответния продукт, няма да се получи желания резултат, защото DISTINCT се прилага върху целия резултатен ред и всяка комбинация ProductName и Price е уникална за таблицата Products. Затова се използва подзаявка, за да се определи кои редове имат една от трите най-високи цени: SELECT ProductID, ProductName, Price FROM Products WHERE price IN ( SELECT DISTINCT TOP 3 price FROM Products ORDER BY price DESC ) ORDER BY price DESC; В този случай редовете се връщат подредени в низходящ ред по цена, тъй като редът се контролира от външната заявка, към която е добавен ORDER BY. Използване на оператори за подзаявки – EXISTS, ANY(или SOME), ALL EXISTS е оператор, който приема подзаявка като аргумент и връща стойност TRUE, ако тази подзаявка връща някакви изходни данни и стойност FALSE – в противен случай. Този оператор не може да върне стойност UNKNOWN. За подзаявките, които използват EXISTS, няма значение коя колона избира EXISTS или дали избира всички колони, защото този оператор просто регистрира дали има някакви изходни данни от подзаявката или не и изобщо не използва генерираните стойности. Например: 1) Заявка, извеждаща идентификаторите и имената на продуктите, които не са продавани и продажбата им не е преустановена. SELECT p.ProductID, p.ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM SaleDetails sd WHERE sd.ProductID = p.ProductID) AND p.Discontinued = 0; Тази заявка е еквивалентна на следната: SELECT p.ProductID, p.ProductName FROM Products p LEFT JOIN SaleDetails sd ON sd.ProductID = p.ProductID WHERE sd.ProductID IS NULL AND p.Discontinued = 0; 2) Заявка, извеждаща имената на продуктите, които са продавани. SELECT p.ProductName FROM Products p WHERE EXISTS (SELECT * FROM SaleDetails sd WHERE sd.ProductID = p.ProductID); Тази заявка е еквивалентна на следната: SELECT DISTINCT p.ProductName FROM Products p INNER JOIN SaleDetails sd ON sd.ProductID = p.ProductID; ANY (или SOME) връща стойност TRUE, ако някоя стойност, избрана от подзаявката, удовлетворява условието на външната заявка. Например: 3) Заявка, извеждаща данните за клиентите, намиращи се в градове, в които има доставчици. SELECT * FROM Customers WHERE City = ANY (SELECT DISTINCT City FROM Suppliers); Тази заявка е еквивалентна на следната: SELECT * FROM Customers WHERE City IN (SELECT DISTINCT City FROM Suppliers); 4) Заявка, извеждаща продуктите с доставна цена, по-голяма от доставната цена на някой (кой да е) продукт от дадена по името си категория. SELECT p.ProductID, p.ProductName, p.Price FROM Products p WHERE p.price > ANY (SELECT DISTINCT price FROM Products p INNER JOIN categories c ON p.CategoryID = c.CategoryID WHERE c.CategoryName = "name") Тази заявка е еквивалентна на следната: SELECT p.ProductID, p.ProductName, p.Price FROM Products p WHERE p.price > (SELECT MIN(price) FROM Products p INNER JOIN categories c ON p.CategoryID = c.CategoryID WHERE c.CategoryName = "name"); Когато подзаявката не генерира изходни данни, ANY връща FALSE. ALL връща стойност TRUE, ако всяка стойност, избрана от подзаявката, удовлетворява условието на външната заявка. Например: 5) Заявка, извеждаща продуктите с доставна цена по-голяма от доставната цена на всеки продукт от дадена по името си категория. SELECT p.ProductID, p.ProductName, p.Price FROM Products p WHERE p.price > ALL (SELECT DISTINCT price FROM Products p INNER JOIN categories c ON p.CategoryID = c.CategoryID WHERE c.CategoryName = "name") Тази заявка не е еквивалентна на следната: SELECT p.ProductID, p.ProductName, p.Price FROM Products p WHERE p.price > (SELECT MAX(price) FROM Products p INNER JOIN categories c ON p.CategoryID = c.CategoryID WHERE c.CategoryName = "name"); Когато подзаявката не генерира изходни данни, ALL връща TRUE. Следователно за пример 5, когато категория със зададеното име не съществува, първата заявка ще изведе данните за всички продукти от таблицата Products, докато втората (с обобщаващата функция MAX) няма да генерира никакви изходни данни. Освен това ако има продукт от зададената категория, на който не е въведена доставна цена (т.е. price има стойност NULL), първата заявка няма да върне резултат, за разлика от втората, която може и да върне някакви изходни данни. 6) Заявка, извеждаща идентификаторите и имената на продуктите, които не са продавани и продажбата им не е преустановена. SELECT p.ProductID, p.ProductName FROM Products p WHERE p.ProductID ALL (SELECT DISTINCT ProductID FROM SaleDetails) AND Discontinued = 0; Взаимосвързани (съпоставени) заявки Взаимосвързаните (съпоставени) заявки използват подзаявки, от които се прави обръщение към таблицата от FROM на външната (основната, главната) заявка. По този начин подзаявката се изпълнява многократно, по един път за всеки ред от таблицата в основната заявка, при което резултатите от всяко изпълнение на подзаявката трябва да бъдат съпоставени със съответния ред от външната заявка. Взаимосвързаните заявки се използват за сравняване на определени редове от една таблица с условие от съответстващата таблица. Например: 1) Заявка, извеждаща всички редове от SaleDetails, за които продажната цена на съответния продукт е по-голяма или равна на средната аритметична стойност на продажните цени на този продукт с поне 50%. SELECT * FROM SaleDetails sd WHERE price >= (SELECT 1.5*AVG(price) FROM SaleDetails sd1 WHERE sd.ProductID = sd1.ProductID); 2) Заявка, извеждаща данните за клиентите, пазарували на дадена дата (21.08.2003г.). SELECT * FROM Customers c WHERE #21/08/2003# IN (SELECT DISTINCT DATEVALUE(SaleDate) FROM Sales s WHERE c.CustomerID = s.CustomerID); Тази заявка е еквивалентна на следната: SELECT DISTINCT c.* FROM Customers c INNER JOIN Sales s ON c.CustomerID = s.CustomerID WHERE DATEDIFF("d", s.SaleDate, #2003/08/21#) = 0; 3) Заявка, която връща продажбите на продуктите, чиито приходи са под 75% от средните приходи на всички продажби на съответния продукт. SELECT sd.SaleID, p.ProductID, p.ProductName, sd.Price*sd.Quantity*(1-sd.Discount) AS ExtendedPrice FROM Products p INNER JOIN SaleDetails sd ON p.ProductID = sd.ProductID WHERE sd.Price*sd.Quantity*(1-sd.Discount) < ( SELECT 0.75*AVG(sd1.Price*sd1.Quantity* (1-sd1.Discount)) FROM SaleDetails sd1 WHERE p.ProductID = sd1.ProductID ); Взаимосвързаните заявки водят до преизчисляване на подзаявката за всеки ред в оператора SELECT на главната заявка, което може да не е най-ефикасния начин за постигане на желания резултат. Всеки път, когато е възможно, оптимизаторът за планиране на заявки в Access обработва заявката, като я преизгражда като съединение между източника, определен във FROM и подзаявката. В много случаи е възможно тази реконструкция да се извърши ръчно, но целта на заявката може да не бъде така ясно определена, както когато проблемът бъде решен чрез подзаявка. Задачи Задача 1. Да се напише заявка, която да връща имената на клиентите, които не са пазарували през изминалия месец. Решение: SELECT CompanyName FROM Customers WHERE CustomerID NOT IN ( SELECT DISTINCT CustomerID FROM Sales WHERE SaleDate BETWEEN DATEADD("m", -1, Date()) AND Date() ) или SELECT CompanyName FROM Customers c WHERE NOT EXISTS ( SELECT * FROM Sales s WHERE s.CustomerID = c.CustomerID AND SaleDate BETWEEN DATEADD("m", -1, Date()) AND Date() ); или SELECT CompanyName FROM Customers WHERE CustomerID ALL ( SELECT DISTINCT CustomerID FROM Sales WHERE SaleDate BETWEEN DATEADD("m", -1, Date()) AND Date() ); Задача 2. Да се напише заявка, която да връща имената на всички продукти, чиято продажба не е преустановена и броя на техните продажби за текущия ден. Решение: SELECT p.ProductName, ( SELECT COUNT(*) FROM SaleDetails sd INNER JOIN Sales s ON sd.SaleID = s.SaleID WHERE DATEDIFF("d", s.SaleDate, DATE()) = 0 AND sd.ProductID = p.ProductID ) AS CountOfSales FROM Products p WHERE p.Discontinued = 0; Задача 3. Да се напише заявка, която да връща имената на всички служители, които не са напуснали и броя на осъществените от тях продажби за текущия ден. Решение: SELECT e.FirstName, e.LastName, ( SELECT COUNT(*) FROM Sales s WHERE DATEDIFF("d", s.SaleDate, DATE()) = 0 AND s.EmployeeID = e.EmployeeID ) AS CountOfSales FROM Employees e WHERE e.TerminationDate IS NULL Задача 4. Да се напише заявка, която да връща датите, на които не е продаван даден по идентификатора си (например 1) продукт. Резултатът да бъде сортиран по дата в низходящ ред. Решение: SELECT DISTINCT DATEVALUE(s.SaleDate) AS Dates FROM Sales s WHERE 1 NOT IN ( SELECT DISTINCT ProductID FROM SaleDetails sd INNER JOIN Sales s1 ON sd.SaleID = s1.SaleID WHERE DATEDIFF("d", s.SaleDate, s1.Saledate) = 0 ) ORDER BY DATEVALUE(s.SaleDate) DESC; или SELECT DISTINCT DATEVALUE(s.SaleDate) AS Dates FROM Sales s WHERE NOT EXISTS ( SELECT * FROM SaleDetails sd INNER JOIN Sales s1 ON sd.SaleID = s1.SaleID WHERE DATEDIFF("d", s.SaleDate, s1.Saledate) = 0 AND sd.ProductID = 1 ) ORDER BY DATEVALUE(s.SaleDate) DESC; Задача 5. Да се напише заявка, която да връща датите, на които са се продавали всички продукти, чиято продажба не е преустановена. Решение: SELECT DISTINCT DATEVALUE(s.SaleDate) AS Dates FROM Sales s WHERE NOT EXISTS ( SELECT * FROM Products p WHERE NOT EXISTS ( SELECT * FROM Sales s1 INNER JOIN SaleDetails sd ON s1.SaleID = sd.SaleID WHERE p.ProductID = sd.ProductID AND DATEDIFF("d",s.SaleDate,s1.SaleDate)=0 ) AND Discontinued = 0 ); Най-вътрешната заявка дава всички продажби на съответната дата; по-външната се съпоставя с нея, за да открие продукт, който не е продаден тогава и продажбата му не е преустановена; най-външната заявка връща всички различни дати, които не са от този списък, т.е. няма непродадени продукти, следователно всички са продавани.
|