Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Объектно-реляционная СУБД PostgreSQL. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
787 Кб
Скачать

[GROUP BY список_полей

[HAVING условие_отбора_групп] ]

[ORDER BY поле_1 [ASC|DESC], поле_2 [ASC|DESC], … [LIMIT число] [OFFSET число]]

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

SELECT [ALL|DISTINCT] * | список_полей/выражений

В списке полей перечислены те поля и/или выражения, значения которых будут выведены в колонках запроса. Если требуется вывести все поля табличного выражения, то список полей обычно заменяется звёздочкой (*).

При установленной опции ALL запрос выведет все полученные строки, даже если они будут идентичны друг другу. Напротив, если задать опцию DISTINCT, то дубликаты строк выводиться не будут. Заметим, что по умолчанию установлена опция ALL, поэтому для вывода всех строк запроса ключевое слово ALL можно не указывать.

Формально только эта строка в запросе является обязательной, поэтому можно использовать оператор SELECT как калькулятор. Например, запрос

SELECT 1+sin(pi()/6);

 

 

 

вычислит результат математического выражения

1 + sin

π

и выведет его

 

 

6

 

значение, равное 1.5. Такой вариант использования SELECT может иметь смысл, если нам нужно получить результат работы некоторой встроенной или написанной разработчиком функции. Например, встроенную функцию random() можно вызвать так:

SELECT random();

При каждом вызове запрос будет возвращать случайное вещественное число из интервала (0;1).

FROM табличное_выражение

31

Табличным выражением может являться как одна таблица, так и соединение нескольких таблиц и/или подзапросов. Соединение таблиц имеет вид:

T1 тип_соединения T2 ON условие_соединения

где T1 и T2 могут в свою очередь быть соединениями. Типы соединений:

А) перекрёстное соединение (CROSS JOIN). В этом случае каждая запись таблицы T1 сцепляется с каждой записью таблицы T2. Набор колонок виртуальной таблицы-соединения последовательно объединяет в себе колонки таблицы T1 и колонки таблицы T2. Если таблицы содержат N и M строк, соединённая таблица будет содержать N * M строк. С точки зрения теории перекрёстное соединение является декартовым произведением записей.

Варианты синтаксиса:

FROM T1, T2

FROM T1 CROSS JOIN T2

Б) внутреннее соединение (INNER JOIN). В этом случае сцепляются те записи из каждой таблицы, для которых выполняется условие соединения. Обычно это совпадение значения внешнего ключа подчинённой таблицы со значением первичного ключа основной таблицы.

Варианты синтаксиса:

FROM T1 INNER JOIN T2 ON T1.fkey = T2.pkey

FROM T1 JOIN T2 ON T1.fkey = T2.pkey

FROM T1, T2 WHERE T1.fkey = T2.pkey

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

Выполним запрос:

SELECT *

FROM Персонал INNER JOIN Отделы

ON Персонал.НомерОтдела = Отделы.НомерОтдела;

или

SELECT *

FROM Персонал, Отделы

32

WHERE Персонал.НомерОтдела = Отделы.НомерОтдела;

Оба запроса выводят один и тот же результат, причём колонка НомерОтдела появляется в результирующей таблице два раза, так как в список вывода будут включены все поля таблицы Сотрудники и все поля таблицы Отделы. Можно явно указать выводимые поля, причём, поскольку имя НомерОтдела не уникально, нужно будет к имени поля добавить ещё имя таблицы Отделы:

SELECT ТабНомер, ФИО, Должность, Отделы.НомерОтдела, НазваниеОтдела

FROM Персонал INNER JOIN Отделы

ON Персонал.НомерОтдела = Отделы.НомерОтдела;

Для внутреннего соединения таблиц, по мнению автора, более наглядным (особенно в случае соединения нескольких таблиц) является третий вариант синтаксиса. В данном запросе предложение FROM можно заменить на

FROM Персонал, Отделы

WHERE Персонал.НомерОтдела = Отделы.НомерОтдела;

Результат выполнения запроса представлен на рис. 6.

Рис. 6. Результат выполнения запроса

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

В) левое соединение (LEFT JOIN). В случае левого соединения в результирующую таблицу входят все записи из левой (первой из указанных в предложении FROM) таблицы, даже если в правой (второй из указанных в предложении FROM) таблице нет связанной записи. В этом случае поля, соответствующие правой таблице, заполняются значениями NULL.

33

Варианты синтаксиса:

FROM T1 LEFT JOIN T2 ON условие_соединения

Приведём пример. Уберём из таблицы Персонал данные о номере отдела, в котором работает менеджер Кузнецов В.В. Тогда предыдущий запрос вернёт только две записи (рис. 7).

Рис. 7. Результат внутреннего соединения таблиц

При этом запрос

SELECT ТабНомер, ФИО, Должность, Отделы.НомерОтдела, НазваниеОтдела

FROM Персонал LEFT JOIN Отделы

ON Персонал.НомерОтдела = Отделы.НомерОтдела;

вернёт данные о всех трёх сотрудниках, однако поле НазваниеОтдела в данных Кузнецова будем пустым (рис. 8).

Рис. 8. Результат левого соединения таблиц

Результат запроса наглядно показывает, что в случае левого соединения таблиц сначала выполняется внутреннее соединение (первые две записи), а затем к результату добавляются все записи таблицы Персонал, которым не

34

соответствуют никакие строки из таблицы Отделы, причём соответствующие колонки остаются пустыми.

Г) правое соединение (RIGHT JOIN). В случае правого соединения в результирующую таблицу входят все записи из правой таблицы, даже если в левой таблице нет связанной записи. В этом случае поля, соответствующие левой таблице, заполняются значениями NULL.

Варианты синтаксиса:

FROM T1 RIGHT JOIN T2 ON условие_соединения

Продолжение примера. После того, как мы удалили НомерОтдела из данных Кузнецова В.В., в отделе с номером 3 ("Менеджеры") формально не осталось ни одного сотрудника. Если теперь в запросе использовать правое соединение то в выводе результатов запроса мы увидим все отделы, в том числе и отдел "Менеджеры". Приведём текст запроса и его результат (рис. 9):

SELECT ТабНомер, ФИО, Должность, Отделы.НомерОтдела, НазваниеОтдела

FROM Персонал RIGHT JOIN Отделы

ON Персонал.НомерОтдела = Отделы.НомерОтдела;

Рис. 9. Результат правого соединения таблиц

Результат запроса наглядно показывает, что сначала выполняется внутреннее соединение (первые две записи), а затем к результату добавляются все записи таблицы Отделы, которым не соответствуют никакие строки из таблицы Персонал, причём соответствующие колонки остаются пустыми.

35

Д) полное соединение (FULL JOIN). Результат полного соединения фактически представляет собой объединение результатов выполнения внутреннего, левого и правого соединения.

Варианты синтаксиса:

FROM T1 FULL JOIN T2 ON условие_соединения

Выполнив запрос

SELECT ТабНомер, ФИО, Должность, Отделы.НомерОтдела, НазваниеОтдела

FROM Персонал FULL JOIN Отделы

ON Персонал.НомерОтдела = Отделы.НомерОтдела;

получим в результате следующую таблицу (рис. 10):

Рис. 10. Результат полного соединения таблиц

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

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

Определить псевдоним таблицы можно, написав

FROM табличное_выражение AS псевдоним

Ключевое слово AS не является обязательным, но его наличие упрощает понимание запроса.

36

Пример:

SELECT *

FROM "очень_длинное_имя_таблицы" As a

JOIN "другое_длинное_имя" As b ON a.id = b.num;

Хотя в основном псевдонимы используются для удобства, они бывают необходимы, когда таблица соединяется сама с собой. Соответствующий пример приведём немного позже.

2.6. Отбор записей в предложении WHERE

Продолжим рассмотрение структуры оператора SELECT.

Отбор записей из результирующей таблицы выполняется c помощью предложения "WHERE условие_отбора_записей".

После соединения таблиц в предложении FROM каждая строка полученной виртуальной таблицы проходит проверку по условию отбора записей. Если результат проверки равен true, то эта строка остаётся в выходной таблице, иначе (если результат равен false или NULL) – отбрасывается.

Вкачестве условий можно использовать:

операции сравнения значений полей (=, <>, !=, <=, >=)

вхождение значения в список:

выражение IN (список_значений/выражений или подзапрос)

– не вхождение значения в список:

выражение NOT IN (список_значений/выражений или подзапрос)

вхождение значения в интервал (включая "крайние" значения): выражение BETWEEN нижнее_значение/выражение

AND верхнее_значение/выражение (при этом должно выполняться условие:

нижнее_значение/выражение <= верхнее_значение/выражение).

не вхождение значения в интервал (включая "крайние" значения): выражение NOT BETWEEN нижнее_значение/выражение

AND верхнее_значение/выражение

37

(при этом должно выполняться условие: нижнее_значение/выражение <= верхнее_значение/выражение).

– сравнение по шаблону:

выражение LIKE шаблон – с учётом регистра символов;

выражение ILIKE шаблон – без учёта регистра символов; выражение NOT LIKE шаблон – с учётом регистра символов; выражение NOT ILIKE шаблон – без учёта регистра символов;

где шаблон – это текстовая строка, включающая как обычные, так и специальные символы:

% – любое количество (в том числе и 0) символов; _ – один произвольный символ.

Можно также проверить вхождение значения выражения в массив значений с помощью условия вида: выражение = ANY(имя_массива). В роли массива может выступать поле таблицы, представляющее собой массив, или переменная типа массив.

Если значение выражения совпадает с некоторым элементом массива, то это условие вернёт значение true, иначе (значение выражения не совпадает ни с одним из элементов массива) вернётся значение false.

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

Кроме ANY и его синонима SOME при работе с массивами можно использовать предикат ALL. Например, если мы хотим проверить, что значение выражения меньше любого элемента массива, то условие можно записать так: выражение < ALL(имя_массива).

Отдельные условия могут использоваться в качестве операндов логических операций AND (логическое И), OR (логическое ИЛИ) и NOT (логическое НЕ). Приоритет логических операций (по убыванию): NOT, AND, OR.

Примеры.

38

2)Вывести данные всех сотрудников отдела 2 с окладом больше 20 000

рублей.

SELECT * FROM Персонал

WHERE НомерОтдела = 2 AND Оклад>20000::money;

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

2) Вывести данные о сотрудниках, родившихся в промежутке от 1 января 1980 года до 31 декабря 1989 года включительно.

SELECT * FROM Персонал

WHERE ДатаРождения BETWEEN ‘1980-01-01’ AND ‘1989-12-31’;

В этом примере PostgreSQL без дополнительных указаний преобразует строки, содержащие дату, в тип date, но для наглядности мы можем явно задать это преобразование:

SELECT * FROM Персонал

WHERE ДатаРождения BETWEEN '1980-01-01'::date AND '1989-12-31'::date;

3) Вывести данные о сотрудниках, чьи фамилии заканчиваются на "ов".

SELECT * FROM Персонал

WHERE ФИО LIKE '%ов %';

Условию LIKE удовлетворяют строки 'Иванов И.П.' и 'Кузнецов В.В.', так как содержат последовательность символов 'о','в' и пробел. Строка 'Петрова М.И.' не соответствует заданному шаблону, так как на месте пробела стоит символ 'а'.

2.7. Группировка записей в запросе. Агрегатные функции

После того, как все записи, удовлетворяющие условию WHERE, отобраны, их можно разбить на группы. В каждую группу объединяются записи с одинаковыми значениями полей, перечисленных в предложении GROUP BY запроса. К полученным группам обычно применяется одна или несколько так называемых агрегатных функций.

39

Перечислим основные их них.

COUNT(имя_поля) – подсчитывает число записей в каждой группе с непустым значением указанного поля. Если нужно просто подсчитать число записей в каждой группе можно использовать COUNT(*).

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

SELECT НомерОтдела, COUNT(*) AS ЧислоСотрудников FROM Персонал

GROUP BY НомерОтдела;

Результат выполнения запроса – таблица из двух столбцов: НомерОтдела и ЧислоСотрудников.

Усложним задание. Пусть кроме номера отдела нужно вывести и его название. Для этого потребуется связать таблицу Персонал и таблицу Отделы.

SELECT Отделы.НомерОтдела, НазваниеОтдела,

COUNT(*) AS ЧислоСотрудников FROM Персонал, Отделы

WHERE Персонал.НомерОтдела = Отделы.НомерОтдела

GROUP BY Отделы.НомерОтдела, НазваниеОтдела;

Замечание. Поле НомерОтдела присутствует и в таблице Персонал, и в таблице Отделы, поэтому в SQL-запросе необходимо дополнить название поля именем таблицы.

MIN(имя_поля) – нахождение минимального из значений поля;

MAX(имя_поля) – нахождение максимального из значений поля.

Пример. Пусть требуется определить величину минимального и максимального оклада по всем сотрудникам фирмы. В этом случае агрегатные функции MIN(Оклад) и MAX(Оклад) будут применяться ко всему набору записей таблицы Сотрудники, поэтому предложение GROUP BY отсутствует в запросе:

SELECT MIN(Оклад) AS МинимальныйОклад, MAX(Оклад) AS МаксимальныйОклад

FROM Персонал;

Если же мы хотим получить эти данные по каждому отделу отдельно, придётся использовать группировку записей:

SELECT НомерОтдела,

MIN(Оклад) AS МинимальныйОклад,

MAX(Оклад) AS МаксимальныйОклад FROM Персонал

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]