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

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

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

Чаще всего аргументы функции являются входными параметрами, однако PostgreSQL допускает также использование в списке аргументов изменяемых и выходных параметров. Для наглядности программист может перед именем аргумента указать одно из ключевых слов IN (для входного параметра), OUT (для выходного параметра) или INOUT (для изменяемого параметра), однако необходимости в этом нет. Заметим только, что если функция имеет не один, а несколько выходных параметров, то в качестве типа возвращаемого значения следует указывать тип record.

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

CREATE FUNCTION square(float, float) RETURNS float AS $$

SELECT $1 * $2; $$ LANGUAGE SQL;

Для улучшения читаемости программы в списке аргументов рекомендуется указывать и имена аргументов, тогда их (имена) можно будет использовать для доступа к переданным значениям:

CREATE FUNCTION square(x float, y float)

RETURNS float AS $$ SELECT x * y;

$$ LANGUAGE SQL;

Замечание. Предположим, что мы сначала реализовали первый вариант функции, а затем решили изменить её, оставив ту же сигнатуру функции (количество аргументов и порядок их типов). При запуске второго запроса мы получим сообщение об ошибке: «функция "square" с аргументами таких типов уже существует». Чтобы избежать появления ошибки, следует указать CREATE OR REPLACE (создать или заменить) в заголовке функции:

CREATE OR REPLACE FUNCTION square(…)

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

Функции расширяют возможности SQL-запросов. Так, например, с помощью функции можно реализовывать запросы с параметрами. В этом

51

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

Приведём пример. Создадим таблицу Города(Название, ЧислоЖителей) и заполним её данными о численности жителей в крупных городах России.

CREATE TABLE Города

(

Название varchar(20) PRIMARY KEY, ЧислоЖителей integer

);

INSERT INTO Города VALUES ('Москва', 11980),

('Санкт-Петербург', 5028), ('Казань', 1176),

('Самара', 1172), ('Волгоград', 1019), ('Саратов', 840), ('Тольятти', 719);

Значение поля ЧислоЖителей в таблице задано в тысячах человек. Напишем функцию, возвращающую Названия и ЧислоЖителей городов с

населением не менее N тысяч человек.

CREATE OR REPLACE FUNCTION sitypeoples (N integer) RETURNS TABLE (Название varchar(20),

ЧислоЖителей integer) AS $$ SELECT Название, ЧислоЖителей FROM Города WHERE ЧислоЖителей >= N;

$$ LANGUAGE SQL

Чтобы увидеть результат, выполним запрос:

SELECT * FROM sitypeoples(1000);

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

52

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

В тех случаях, когда возможностей языка SQL не хватает для решения задачи, пользователь может написать свою функцию на одном из языковых расширений PostgreSQL. Обычно используется расширение PL/pgSQL, которое является полноценным языком программирования. Функции на языке PL/pgSQL кроме SQL-операторов могут содержать описания переменных и управляющие конструкции языка.

3.2. Особенности языка PL/pgSQL. Объявление переменных

Тело функции, написанной на PL/pgSQL, представляет собой блок. Структура блока:

[<<метка>>]

[DECLARE объявление переменных] BEGIN

операторы тела блока END [метка];

Каждое объявление и каждый оператор в блоке должны завершаться символом ";" (точка с запятой). Тело блока может представлять собой просто последовательность операторов, а может быть разбито на внутренние блоки. Любой внутренний блок также должен иметь точку с запятой после END, как показано выше. Однако основной блок, представляющий собой тело функции, точки с запятой не требует.

Метка нужна, чтобы идентифицировать блок в операторе досрочного выхода EXIT, или чтобы иметь возможность ссылаться на переменные, объявленные в этом блоке.

53

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

В коде PL/pgSQL, так же как и в SQL-операторах, можно использовать комментарии двух видов:

однострочные комментарии: двойной дефис (--) начинает комментарий, который завершается в конце строки.

блочные комментарии: начинаются с /* и завершаются */. Блочные комментарии могут быть вложенными.

Для объявления переменных используется раздел DECLARE. Заметим, что в PostgreSQL каждая переменная объявляется отдельно.

Пример.

DECLARE

переменная тип [NOT NULL] [ DEFAULT значение];

Пример. Пусть требуется объявить переменные i, j, k типа integer. Вариант DECLARE i, j, k integer; вызовет сообщение об ошибке:

ОШИБКА: отсутствует определение типа данных.

Правильный вариант:

DECLARE

i integer; j integer; k integer;

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

В набор типов данных входят как встроенные типы, с которыми мы уже знакомы, так и типы, определённые пользователем. Кроме того, имя каждой таблицы базы данных PostgreSQL рассматривает как имя составного типа, структура которого совпадает со структурой строки таблицы. Для задания такого типа используется ключевое слово %ROWTYPE. Для ссылки на имя

54

поля в переменной типа tablename%ROWTYPE используется символ точка (.) по аналогии с составными структурами в языках программирования. Также можно объявлять переменные, тип которых соответствует полю таблицы. Для этого используется ключевое слово %TYPE.

Для иллюстрации изложенного напишем функцию f_test, которая получает табельный номер сотрудника и возвращает значение атрибута ФИО этого сотрудника.

CREATE OR REPLACE FUNCTION f_test(num integer) RETURNS text AS $$

DECLARE

S Персонал%ROWTYPE;

NПерсонал.ФИО%TYPE;

BEGIN

SELECT * INTO S FROM Персонал WHERE ТабНомер = num;

N= S.ФИО;

RETURN N; END

$$ LANGUAGE plpgsql;

В разделе DECLARE можно инициализировать переменную сразу же при её объявлении, используя инструкцию DEFAULT или синтаксис оператора присваивания.

Пример.

DECLARE

N integer DEFAULT 10;

sum real := 0; -- или sum real = 0;

Чтобы объявить массив элементов какого-либо типа, к имени типа дописывается одна или несколько пар открывающих и закрывающих квадратных скобок. Одновременно с объявлением массива можно задать значения его элементов. Варианты инициализации массива приведены в следующем примере:

55

DECLARE

y integer[] DEFAULT ARRAY[3,4,5]; или y integer[] = ARRAY[3,4,5];

или y integer[] = '{1, 2, 3}';

или y integer[] = '[0:2]={1, 2, 3}';

Вдвух первых вариантах для инициализации массива используется конструктор массива ARRAY. Выражение ARRAY[3,4,5] создаёт одномерный массив из трёх элементов и инициализирует его значениями y[1] = 3, y[2] = 4, y[3] = 5 (нумерация элементов массива по умолчанию начинается с единицы).

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

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

Приведём пример. Напишем функцию, в которой создадим массив y как ARRAY[3,4,5], а в теле функции с помощью оператора присваивания добавим в него ещё два элемента: y[0] = 2; y[7] = -1; Полученный массив вернём из функции.

CREATE OR REPLACE FUNCTION test() RETURNS integer[] AS $$

DECLARE

y integer[] = ARRAY[3,4,5]; BEGIN

y[0] = 2; y[7] = -1;

RETURN y; -- или RETURN y[0:7]; END

$$ LANGUAGE plpgsql;

56

Выведем результат вызова функции на панель вывода:

SELECT test();

Получим строку [0:7]={2, 3, 4, 5, NULL, NULL, NULL, -1} в случае возврата всего массива y, или строку {2, 3, 4, 5, NULL, NULL, NULL, -1} в случае возврата среза массива y[0:7], где значениями NULL обозначены неинициализированные элементы массива.

Многомерные массивы PostgreSQL рассматривает как массив массивов, в котором все подмассивы имеют одинаковую длину. При создании многомерных массивов с помощью конструктора можно оставить только внешний ARRAY, так, например, ARRAY[ARRAY[3,-4,12], ARRAY[1,7,0]] эквивалентно ARRAY[[3,-4,12], [1,7,0]]. Для задания многомерного массива также можно использовать текстовый литерал вида '{{список значений подмассива_1}, {список значений подмассива_2}, …}' с указанием или без указания диапазонов индексов. Пример:

DECLARE

A integer[][] = '{{3,-4,12},{1,7,0}}';

B integer[][] = '[3:4][-2:0]={{3,-4,12},{1,7,0}}';

Оба массива содержат одни и те же элементы. Но в массиве A будет стандартная нумерация ([1:2] для строк и [1:3] для столбцов), а в массиве B – заданная программистом ([3:4]для строк и [-2:0] для столбцов).

PostgreSQL содержит целый ряд операций и функций для работы с массивами. К массивам можно применять операции сравнения: =, <>, <, <=, >, >=, а, следовательно, записи можно отсортировать по полю типа массив.

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

Операции (идентификаторы A и B обозначают имена массивов): A[i] доступ к элементу одномерного массива,

A[i1][i2]…[ik] доступ к элементу многомерного массива,

A[n:m]

срез одномерного массива,

A[n1:m1][n2:m2]…[nk:mk]

срез многомерного массива,

A <@ B

проверка вхождения массива A в массив B,

A @> B

проверка вхождения массива B в массив A,

57

A && B возвращает true, если массивы A и B имеют хотя бы один общий элемент,

A || B слияние (конкатенация) двух массивов или массива и значения. Пример. Покажем, как можно расширить многомерный массив с

использованием оператора конкатенации:

CREATE OR REPLACE FUNCTION test() RETURNS integer[][] AS $$ DECLARE

y integer[][] = ARRAY[[1,3],[2,4],[3,5]];

BEGIN

y = y || ARRAY[[4,6]]; RETURN y;

END

$$ LANGUAGE plpgsql; SELECT test();

Возвращаемый результат – строка {{1,3},{2,4},{3,5},{4,6}}

3.3. Функции для работы с массивами

Проиллюстрируем использование функций на примере двух двумерных массивов:

A integer[][] = '{{3,-4,12},{1,7,0}}';

B integer[][] = '[3:4][-2:0]={{3,-4,12},{1,7,0}}';

Текущие размеры и индексацию массива можно получить с помощью функции array_dims, которая возвращает в виде текстовой строки диапазоны изменения индекса для каждой размерности:

array_dims(A) возвращает строку [1:2][1:3] array_dims(B) возвращает строку [3:4][-2:0]

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

Чтобы получить числовые значения нижних и верхних границ диапазонов индексов можно использовать функции array_lower и array_upper,

58

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

array_lower(B,1) равно 3; array_upper(B,1) равно 4; array_lower(B,2) равно -2; array_upper(B,2) равно 0;

Функция array_length(массив, номер_размерности) возвращает число элементов в указанной размерности массива. Например, для двумерного массива B выражение array_length(B,1) возвращает 2 – число его строк, а выражение array_length(B,2) возвращает 3 – число его столбцов.

Функция unnest(массив) превращает массив в столбец данных. Эта функция позволяет применять к массиву стандартные агрегатные функции, например:

SELECT AVG(w) FROM unnest (ARRAY[1,2,3,4,5]) As w;

В запросе вычисляется среднее по значениям столбца w, который формирует функция unnest (имя столбца указывается в самом запросе). Результат – число 3

– выводится в вещественном виде с шестнадцатью нулями. Для форматирования вывода можно использовать приведение типа:

SELECT AVG(w)::numeric(5,2)

FROM unnest (ARRAY[1,2,3,4,5]) As w;

3.4. Управляющие конструкции языка PL/pgSQL

Оператор присваивания. Выше мы уже пользовались оператором присваивания, который имеет следующие варианты

левая_часть := выражение; левая_часть = выражение;

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

59

Пример.

 

x =

random();

x – переменная типа float

i =

(10*x)::int;

i – переменная типа integer

или i = CAST (10*x AS int);

В данном примере вещественное число 10*x преобразуется к ближайшему целому значению и записывается в переменную i. Заметим, что для преобразования вещественного числа к целому можно использовать встроенные функции trunc(x), floor(x), ceil(x), round(x), которые явно определяют результат преобразования:

trunc(x) – отбрасывает дробную часть x;

floor(x) – возвращает наибольшее целое число, меньшее или равное x; ceil(x) – возвращает наименьшее целое число, большее или равное x; round(x) – возвращает целое число, ближайшее к x.

Пример:

SELECT

 

trunc(-3.27),

– выводит -3;

floor(-3.27),

– выводит -4;

ceil(9.84),

– выводит 10;

round(9.84);

– выводит 10;

В следующем примере выполняется конкатенация (соединение) текстовой строки и числа:

SELECT 'x = ' || 2.71;

Результатом будет текстовая строка 'x = 2.71'. Можно использовать и явное преобразование типов:

SELECT ' x = ' || 2.71::text;

SELECT ' x = ' || CAST(2.71 AS text);

Оператор ветвления. Этот оператор позволяет в зависимости от истинности или ложности некоторого условия выбрать один из двух вариантов выполнения кода программы. Синтаксис оператора:

60

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