Опубликован: 10.10.2005 | Уровень: специалист | Доступ: платный | ВУЗ: Московский государственный университет имени М.В.Ломоносова
Лекция 5:

Группировка и условия раздела HAVING, порождаемые и соединенные таблицы

Ссылки на порождаемые таблицы в разделе FROM

В этом разделе мы приведем несколько примеров запросов, в разделе FROM которых содержатся выражения запросов ( ссылки на порождаемые таблицы, см. раздел "Общие синтаксические правила построения скалярных выражений" лекции 13).

Еще один способ формулировки запросов

Прежде всего, на простом примере покажем, как использование ссылок на порождаемые таблицы расширяет возможности формулировки запросов.

SELECT MNG.DEPT_NO, MNG.MNG_NAME
FROM (SELECT DEPT.DEPT_NO, EMP.DEPT_NO, EMP_NAME, EMP_SAL
	FROM DEPT, EMP
	WHERE DEPT.DEPT_MNG = EMP.EMP_NO) 
	AS MNG (DEPT_NO_1, DEPT_NO_2, MNG_NAME, MNG_SAL)
WHERE DEPT_NO_1 = DEPT_NO_2
	AND MNG_SAL = (SELECT MAX (EMP_SAL)
	  FROM EMP
	  WHERE EMP.DEPT_NO = DEPT_NO_1);
15.14. Найти номера отделов и имена руководителей отделов, которые числятся в тех же отделах, которыми руководят, и получают зарплату, размер которой является максимальным для служащих данного отдела.

В этом запросе порождаемая таблица MNG содержит по одной строке для каждого служащего, являющегося руководителем отдела. Первый столбец этой таблицы - DEPT_NO_1 - содержит номер отдела, которым руководит данный служащий. В столбце DEPT_NO_2 хранятся номера отделов, в которых числятся руководители отделов, а в столбцах EMP_NAME и EMP_SAL содержатся имя служащего-руководителя отдела и размер его заработной платы соответственно.

Конечно, этот запрос можно сформулировать и без использования ссылки на порождаемую таблицу в разделе FROM, например, следующим образом ( пример 15.14.1):

SELECT DEPT.DEPT_NO, EMP.EMP_NAME
FROM DEPT, EMP 
	WHERE DEPT.DEPT_MNG = EMP.EMP_NO
	  AND DEPT.DEPT_NO = EMP.DEPT_NO
	  AND EMP.EMP_SAL = (SELECT MAX(EMP_SAL)
	    FROM EMP
	    WHERE EMP.DEPT_NO = DEPT.DEPT_NO);
15.14.1.

А вот как можно сформулировать тот же запрос с использованием раздела WITH ( пример 15.14.2):

WITH MNG (DEPT_NO_1, DEPT_NO_2, MNG_NAME, MNG_SAL) AS
	(SELECT DEPT.DEPT_NO, EMP.DEPT_NO, EMP_NAME, EMP_SAL
	FROM DEPT, EMP
	WHERE DEPT.MNG_NO = EMP.EMP_NO),
	MAX_DEPT_SAL (MAX_SAL, DEPT_NO) AS
	(SELECT MAX (EMP_SAL), DEPT_NO
	FROM EMP
	WHERE DEPT_NO IS NOT NULL
	GROUP BY DEPT_NO)
SELECT DEPT_NO_1, MNG_NAME
FROM MNG
WHERE DEPT_NO_1 = DEPT_NO_2
	AND MNG_SAL = (SELECT MAX_SAL
	  FROM MAX_DEPT_SAL
	  WHERE MAX_DEPT_SAL.DEPT_NO = DEPT_NO_1);
15.14.2.

Случаи, в которых без порождаемых таблиц обойтись невозможно

На самом деле, пример 15.14 демонстрирует лишь возможность альтернативных формулировок запросов с использованием ссылок на порождаемые таблицы в разделе FROM. Но в некоторых случаях без подобных конструкций просто невозможно обойтись. Вот простой пример.

SELECT SUM (TOTAL_EMP), MAX_SAL
FROM (SELECT MAX (EMP_SAL), COUNT (*)
	FROM EMP
	WHERE DEPT_NO IS NOT NULL
	GROUP BY DEPT_NO ) AS DEPT_MAX_SAL (MAX_SAL, TOTAL_EMP)
GROUP BY MAX_SAL;
15.15. Найти общее число служащих и максимальный размер зарплаты в отделах с одинаковым максимальным размером зарплаты.

И в этом случае выражение запросов, содержащееся в разделе FROM, можно перенести в раздел WITH ( пример 15.15.1):

WITH DEPT_MAX_SAL (MAX_SAL, TOTAL_EMP) AS
	(SELECT MAX (EMP_SAL), COUNT (*)
	  FROM EMP
	  WHERE DEPT_NO IS NOT NULL
	  GROUP BY DEPT_NO)
SELECT SUM (TOTAL_EMP), MAX_SAL
FROM DEPT_MAX_SAL
GROUP BY MAX_SAL;
15.15.1.

Здесь мы не можем обойтись "одноуровневой" конструкцией запроса, поскольку требуется двойная группировка, причем вторая группировка должна быть получена в соответствии с результатами первой. Еще один пример.

SELECT COUNT (*), PRO_EDATE, AVG_SAL
FROM (SELECT PRO_EDATE, AVG (EMP_SAL)
	FROM (SELECT PRO_SDATE + PRO_DURAT, PRO_NO
	  FROM PRO) AS PRO1 (PRO_EDATE, PRO_NO), EMP
	WHERE PRO1.PRO_NO = EMP.PRO_NO
	GROUP BY PRO1.PRO_NO ) AS PRO_AVG_SAL (PRO_EDATE, AVG_SAL)
GROUP BY PRO_EDATE, AVG_SAL;
15.16. Найти число проектов, дату их завершения и средний размер зарплаты служащих, участвующих в проекте, для проектов с одной и той же датой завершения и одним и тем же средним размером зарплаты служащих, участвующих в проекте.

Заметим, что выражение запросов на третьей и четвертой строках примера необходимо только по той причине, что нам требуется группировка по дате окончания проектов, соответствующий столбец в таблице PRO отсутствует, а в списке группировки можно использовать только имена столбцов. Для упрощения вида формулировки это выражение разумно вынести в раздел WITH ( пример 15.16.1):

WITH PRO1 (PRO_EDATE, PRO_NO) AS 
	(SELECT PRO_SDATE + PRO_DURAT, PRO_NO
	  FROM PRO)
SELECT COUNT (*), PRO_EDATE, AVG_SAL
FROM (SELECT PRO_EDATE, AVG (EMP_SAL)
	FROM PRO1, EMP
	WHERE PRO1.PRO_NO = EMP.PRO_NO
	GROUP BY PRO1.PRO_NO) AS PRO_AVG_SAL (PRO_EDATE, AVG_SAL)

GROUP BY PRO_EDATE, AVG_SAL;
15.16.1.
Алексей Ковтун
Алексей Ковтун

При попытке исполнения запроса:

CREATE DOMAIN EMP_NO AS INTEGER

    CHECK (VALUE BETWEEN 1 AND 10000);

Выдается ошибка: Неизвестный тип объекта "DOMAIN" в интсрукции CREATE, DROP или ALTER. 

Используется SQL Server MS SQL 2008R2

Александра Каева
Александра Каева