Файл: Практика - Элементы языка SQL.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 03.04.2021

Просмотров: 408

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

например, так: 

SELECT 
    P.NAME AS PNAME, 
    C.NAME AS CNAME, 
    DETAILS.DNAME, 
    CD.VOLUME 
  FROM 
    CONTRAGENTS P, 
    CONTRAGENTS C, 
    DETAILS NATURAL JOIN CD 
  WHERE 
    P.NUM = CD.PNUM AND 
    C.NUM = CD.CNUM; 

Использование агрегатных функций в запросах 

Пример 21. Получить общее количество поставщиков (ключевое слово 

COUNT

): 

SELECT COUNT(*) AS N 
  FROM P; 

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

Пример  22

.  Получить  общее,  максимальное,  минимальное  и  среднее  количества 

поставляемых деталей (ключевые слова 

SUM

MAX

MIN

AVG

): 

SELECT 
    SUM(PD.VOLUME) AS SM, 
    MAX(PD.VOLUME) AS MX, 
    MIN(PD.VOLUME) AS MN, 
    AVG(PD.VOLUME) AS AV 
  FROM PD; 

В результате получим следующую таблицу с одной строкой: 

SM  M

M

AV 

200

100

10

333.33333333 

Использование агрегатных функций с группировками 

Пример 23. Для каждой детали получить суммарное поставляемое количество (ключевое 
слово 

GROUP BY

…): 

SELECT 
    PD.DNUM, 


background image

    SUM(PD.VOLUME) AS SM 
  GROUP BY PD.DNUM; 

Этот запрос будет выполняться следующим образом. Сначала строки исходной таблицы 
будут  сгруппированы  так,  чтобы  в  каждую  группу  попали  строки  с  одинаковыми 
значениями  DNUM.  Потом  внутри  каждой  группы  будет  просуммировано  поле 
VOLUME.  От  каждой  группы  в  результатирующую  таблицу  будет  включена  одна 
строка: 

DNU

SM 

125

450 

300 

Замечание. В списке отбираемых полей оператора SELECT, содержащего раздел GROUP 
BY  можно  включать 

только 

агрегатные  функции  и  поля, 

которые  входят  в  условие 

группировки

. Следующий запрос выдаст синтаксическую ошибку: 

SELECT 
    PD.PNUM, 
    PD.DNUM, 
    SUM(PD.VOLUME) AS SM 
  GROUP BY PD.DNUM; 

Причина ошибки в том, что в список отбираемых полей включено поле PNUM, которое 

не  входит 

в раздел  GROUP  BY.  И  действительно,  в  каждую  полученную  группу  строк 

может  входить  несколько  строк  с 

различными 

значениями  поля  PNUM.  Из  каждой 

группы  строк  будет  сформировано  по  одной  итоговой  строке.  При  этом  нет 
однозначного  ответа  на  вопрос,  какое  значение  выбрать  для  поля  PNUM  в  итоговой 
строке. 

Замечание. Некоторые диалекты SQL не считают это за ошибку. Запрос будет выполнен, 
но  предсказать,  какие  значения  будут  внесены  в  поле  PNUM  в  результатирующей 
таблице, невозможно. 

Пример  24

.  Получить  номера  деталей,  суммарное  поставляемое  количество  которых 

превосходит 400 (ключевое слово 

HAVING

…): 

Замечание. Условие, что суммарное поставляемое количество должно быть больше 400 
не  может  быть  сформулировано  в  разделе  WHERE,  т.к.  в  этом  разделе  нельзя 
использовать агрегатные функции. Условия, использующие агрегатные функции должны 
быть размещены в специальном разделе HAVING: 

SELECT 
    PD.DNUM, 
    SUM(PD.VOLUME) AS SM 
  GROUP BY PD.DNUM 
  HAVING SUM(PD.VOLUME) > 400; 

В результате получим следующую таблицу: 


background image

DNU

SM 

125

450 

Замечание.  В  одном  запросе  могут  встретиться  как  условия  отбора  строк  в  разделе 
WHERE, так и условия  отбора групп в разделе HAVING. Условия отбора групп нельзя 
перенести  из  раздела  HAVING  в  раздел  WHERE.  Аналогично  и  условия  отбора  строк 
нельзя  перенести  из  раздела  WHERE  в  раздел  HAVING,  за  исключением  условий, 
включающих поля из списка группировки GROUP BY. 

Использование подзапросов 

Очень  удобным  средством,  позволяющим  формулировать  запросы  более  понятным 
образом,  является  возможность  использования  подзапросов,  вложенных  в  основной 
запрос. 

Пример  25

.  Получить  список  поставщиков,  статус  которых  меньше  максимального 

статуса в таблице поставщиков (сравнение с подзапросом): 

SELECT * 
  FROM P 
  WHERE P.STATYS < 
    (SELECT MAX(P.STATUS) 
        FROM P); 

Замечание.  Т.к.  поле  P.STATUS  сравнивается  с  результатом  подзапроса,  то  подзапрос 
должен быть сформулирован так, чтобы возвращать таблицу, состоящую 

ровно из одной 

строки и одной колонки

Замечание.  Результат  выполнения  запроса  будет  эквивалентен  результату  следующей 
последовательности действий: 

1.

 

Выполнить 

один раз 

вложенный подзапрос и получить максимальное значение статуса. 

2.

 

Просканировать  таблицу  поставщиков  P,  каждый  раз  сравнивая  значение  статуса 

поставщика  с  результатом  подзапроса,  и  отобрать  только  те  строки,  в  которых 
статус меньше максимального. 

Пример 26

. Использование предиката 

IN

. Получить список поставщиков, поставляющих 

деталь номер 2: 

SELECT * 
  FROM P 
  WHERE P.PNUM IN 
    (SELECT DISTINCT PD.PNUM 
        FROM PD 
        WHERE PD.DNUM = 2); 

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


background image

Замечание.  Результат  выполнения  запроса  будет  эквивалентен  результату  следующей 
последовательности действий: 

1.

 

Выполнить 

один раз 

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

поставляющих деталь номер 2. 

2.

 

Просканировать  таблицу  поставщиков  P,  каждый  раз  проверяя,  содержится  ли  номер 

поставщика в результате подзапроса. 

Пример  27

.  Использование  предиката 

EXIST

.  Получить  список  поставщиков, 

поставляющих деталь номер 2: 

SELECT * 
  FROM P 
  WHERE EXIST 
    (SELECT * 
        FROM PD 
        WHERE 
            PD.PNUM = P.PNUM AND 
            PD.DNUM = 2); 

Замечание.  Результат  выполнения  запроса  будет  эквивалентен  результату  следующей 
последовательности действий: 

1.

 

Просканировать  таблицу  поставщиков  P, 

каждый  раз  выполняя  подзапрос 

с  новым 

значением номера поставщика, взятым из таблицы P. 

2.

 

В результат запроса включить только те строки из таблицы поставщиков, для которых 

вложенный подзапрос вернул непустое множество строк. 

Замечание.  В  отличие  от  двух  предыдущих  примеров,  вложенный  подзапрос  содержит 
параметр  (внешнюю  ссылку),  передаваемый  из  основного  запроса  -  номер  поставщика 
P.PNUM. Такие подзапросы называются 

коррелируемыми 

(

correlated

). Внешняя ссылка 

может  принимать  различные  значения  для  каждой  строки-кандидата,  оцениваемого  с 
помощью  подзапроса,  поэтому  подзапрос  должен  выполняться  заново  для  каждой 
строки,  отбираемой  в  основном  запросе.  Такие  подзапросы  характерны  для  предиката 
EXIST, но могут быть использованы и в других подзапросах. 

Замечание.  Может  показаться,  что  запросы,  содержащие  коррелируемые  подзапросы 
будут  выполняться  медленнее,  чем  запросы  с  некоррелируемыми  подзапросами.  На 
самом деле это не так, т.к. то, как пользователь, сформулировал запрос, 

не определяет

как  этот  запрос  будет  выполняться.  Язык  SQL  является  непроцедурным,  а 
декларативным.  Это  значит,  что  пользователь,  формулирующий  запрос,  просто 
описывает, 

каким должен быть результат запроса

, а как этот результат будет получен - 

за это отвечает сама СУБД. 

Пример  28

.  Использование  предиката 

NOT  EXIST

.  Получить  список  поставщиков,  не 

поставляющих деталь номер 2: 

SELECT * 
  FROM P 
  WHERE NOT EXIST 
    (SELECT * 
        FROM PD 


background image

        WHERE 
            PD.PNUM = P.PNUM AND 
            PD.DNUM = 2); 

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

Пример 29

. Получить имена поставщиков, поставляющих все детали: 

SELECT DISTINCT PNAME 
  FROM P 
  WHERE NOT EXIST 
    (SELECT * 
        FROM D 
        WHERE NOT EXIST 
          (SELECT * 
              FROM PD 
              WHERE 
                  PD.DNUM = D.DNUM AND 
                  PD.PNUM = P.PNUM)); 

Замечание.  Данный  запрос  содержит  два  вложенных  подзапроса  и  реализует 
реляционную операцию 

деления отношений

Самый внутренний подзапрос параметризован двумя параметрами (D.DNUM, P.PNUM) 
и  имеет  следующий  смысл:  отобрать  все  строки,  содержащие  данные  о  поставках 
поставщика с номером PNUM детали с номером DNUM. Отрицание NOT EXIST говорит 
о том, что данный поставщик не поставляет данную деталь. Внешний к нему подзапрос, 
сам являющийся вложенным и параметризованным параметром P.PNUM, имеет смысл: 
отобрать  список  деталей,  которые  не  поставляются  поставщиком  PNUM.  Отрицание 
NOT  EXIST  говорит  о  том,  что  для  поставщика  с  номером  PNUM  не  должно  быть 
деталей, которые не поставлялись бы этим поставщиком. Это в точности означает, что во 
внешнем запросе отбираются только поставщики, поставляющие все детали. 

Использование объединения, пересечения и разности 

Пример  30.  Получить  имена  поставщиков,  имеющих  статус,  больший  3  или 
поставляющих хотя бы одну деталь номер 2 (объединение двух подзапросов - ключевое 
слово 

UNION

): 

SELECT P.PNAME 
  FROM P 
  WHERE P.STATUS > 3 
  UNION 
  SELECT P.PNAME 
    FROM P, PD 
    WHERE P.PNUM = PD.PNUM AND 
        PD.DNUM = 2; 

Замечание.  Результатирующие  таблицы  объединяемых  запросов  должны  быть