Функции Transact-SQL
Функции языка Transact-SQL могут быть агрегатными или скалярными. Эти типы функций рассматриваются в этой статье.
Агрегатные функции
Агрегатные функции выполняют вычисления над группой значений столбца и всегда возвращают одно значение результата этих вычислений. Язык Transact-SQL поддерживает несколько обычных агрегатных функций:
AVG
Вычисляет среднее арифметическое значение данных, содержащихся в столбце. Значения, над которыми выполняется вычисление, должны быть числовыми.
MIN и MAX
Определяют максимальное и минимальное значение из всех значений данных, содержащихся в столбце. Значения могут быть числовыми, строковыми или временными (дата/время).
SUM
Вычисляет общую сумму значений в столбце. Значения, над которыми выполняется вычисление, должны быть числовыми.
COUNT
Подсчитывает количество значений, отличных от null в столбце. Функция count(*) является единственной агрегатной функцией, которая не выполняет вычисления над столбцами. Эта функция возвращает количество строк (независимо от того, содержат ли отдельные столбцы значения null).
COUNT_BIG
Аналогична функции count, с той разницей, что возвращает значение данных типа BIGINT.
Использование обычных агрегатных функций в инструкции SELECT будет рассматриваться в одной из следующих статей.
Скалярные функции
Скалярные функции Transact-SQL используются в создании скалярных выражений. (Скалярная функция выполняет вычисления над одним значением или списком значений, тогда как агрегатная функция выполняет вычисления над группой значений из нескольких строк.) Скалярные функции можно разбить на следующие категории:
- числовые функции;
- функции даты;
- строковые функции;
- системные функции;
- функции метаданных.
Эти типы функций рассматриваются в последующих разделах.
Числовые функции
Числовые функции языка Transact-SQL — это математические функции для модифицирования числовых значений. Список числовых функций и их краткое описание приводится в таблице ниже:
Возвращает абсолютное значение (т. е. отрицательные значения возвращаются, как положительные) числового выражения n.
SELECT ABS(-5.320) -- Вернет 5.320 SELECT ABS(8.90) -- Вернет 8.90
Обратные тригонометрические функции, вычисляющие арккосинус, арксинус, арктангенс значения n (для ATN2 вычисляется арктангенс n/m). Исходные значения n, m и результат имеют тип данных FLOAT.
Тригонометрические функции, вычисляющие косинус, синус, тангенс, котангенс значения n. Результат имеет тип данных FLOAT.
Функция DEGREES преобразует радианы в градусы, RADIANS соответственно наоборот.
SELECT DEGREES(PI() / 4) -- Вернет 45 SELECT COS(RADIANS(60.0)) -- Вернет 0.5
Округляет число до большего целого значения.
SELECT CEILING(-5.320) -- Вернет -5 SELECT CEILING(8.90) -- Вернет 9
Округляет значение n с точностью до p. Когда аргумент p положительное число, округляется дробная часть числа n, а когда отрицательное — целая часть. При использовании необязательного аргумента t, число n не округляется, а усекается (т.е. округляется в меньшую сторону).
SELECT ROUND(5.3208, 3) -- Вернет 5.3210 SELECT ROUND(125.384, -1) -- Вернет 130.000 SELECT ROUND(125.384, -1, 1) -- Вернет 120.000
Округляет до меньшего целого значения.
SELECT FLOOR(5.88) -- Вернет 5
Вычисляет значение e n .
LOG(n) — вычисляет натуральный логарифм (т.е. с основанием e) числа n, LOG10(n) — вычисляет десятичный (с основанием 10) логарифм числа n.
Возвращает значение π (3,1415).
Вычисляет значение x y .
Возвращает произвольное число типа FLOAT в диапазоне значений между 0 и 1.
Возвращает количество строк таблицы, которые были обработаны последней инструкцией Transact-SQL, исполненной системой. Возвращаемое значение имеет тип BIGINT.
Возвращает знак значения n в виде числа: +1, если положительное, -1, если отрицательное.
SQRT(n) — вычисляет квадратный корень числа n, SQUARE(n) — возвращает квадрат аргумента n.
Функции даты
Функции даты вычисляют соответствующие части даты или времени выражения или возвращают значение временного интервала. Поддерживаемые в Transact-SQL функции даты и их краткое описание приводятся в таблице ниже:
Возвращает текущую системную дату и время.
SELECT GETDATE()
Возвращает указанную в параметре item часть даты date в виде целого числа.
-- Вернет 1 (Январь) SELECT DATEPART(month, '01.01.2012') -- Вернет 4 (Wednesday) SELECT DATEPART(weekday, '02.01.2012')
Возвращает указанную в параметре item часть даты date в виде строки символов.
-- Вернет January SELECT DATENAME(month, '01.01.2012') -- Вернет Wednesday SELECT DATENAME(weekday, '02.01.2012')
Вычисляет разницу между двумя частями дат dat1 и dat2 и возвращает целочисленный результат в единицах, указанных в аргументе item.
-- Вернет 19 (19 лет промежуток между датами) SELECT DATEDIFF(year, '01.01.1990', '01.01.2010') -- Вернет 7305 (7305 дней промежуток между датами) SELECT DATEDIFF(day, '01.01.1990', '01.01.2010')
Прибавляет n-е количество единиц, указанных в аргументе item к указанной дате date. (Значение аргумента n также может быть отрицательным.)
-- Прибавит 3 дня к текущей дате SELECT DATEADD(day, 3, GETDATE())
Строковые функции
Строковые функции манипулируют значениями столбцов, которые обычно имеют символьный тип данных. Поддерживаемые в Transact-SQL строковые функции и их краткое описание приводятся в таблице ниже:
Преобразовывает указанный символ в соответствующее целое число кода ASCII.
SELECT ASCII('W') -- 87 SELECT UNICODE('ю') -- 1102
Преобразовывает код ASCII (или Unicode если NCHAR) в соответствующий символ.
SELECT CHAR(87) -- 'W' SELECT NCHAR(1102) -- 'ю'
Возвращает начальную позицию вхождения подстроки str1 в строку str2. Если строка str2 не содержит подстроки str1, возвращается значение 0
-- Вернет 5 SELECT CHARINDEX ('морф', 'полиморфизм')
Возвращает целое число от 0 до 4, которое является разницей между значениями SOUNDEX двух строк str1 и str2. Метод SOUNDEX возвращает число, которое характеризует звучание строки. С помощью этого метода можно определить подобно звучащие строки. Работает только для символов ASCII.
-- Вернет 2 SELECT DIFFERENCE ('spelling', 'telling')
Возвращает количество первых символов строки str, заданное параметром length для LEFT и последние length символов строки str для функции RIGHT.
DECLARE @str nvarchar(30) = 'Синхронизация'; -- Вернет 'Синх' SELECT LEFT(@str, 4) -- Вернет 'зация' SELECT RIGHT(@str, 5)
Возвращает количество символов (не количество байт) строки str, указанной в аргументе, включая конечные пробелы.
Функция LOWER преобразовывает все прописные буквы строки str1 в строчные. Входящие в строку строчные буквы и иные символы не затрагиваются. Функция UPPER преобразовывает все строчные буквы строки str в прописные.
DECLARE @str nvarchar(30) = 'Синхронизация'; -- Вернет 'СИНХРОНИЗАЦИЯ' SELECT UPPER(@str) -- Вернет 'синхронизация' SELECT LOWER(@str)
Функция LTRIM удаляет начальные пробелы в строке str, RTRIM соответственно удаляет пробелы в конце строки.
Возвращает строку в кодировке Unicode с добавленными ограничителями, чтобы преобразовать строку ввода в действительный идентификатор с ограничителями.
DECLARE @str nvarchar(30) = 'Синхронизация'; -- Вернет '[Синхронизация]' SELECT QUOTENAME(@str)
Возвращает начальную позицию первого вхождения шаблона p в заданное выражение expr, или ноль, если данный шаблон не обнаружен.
-- Вернет 4 SELECT PATINDEX('%хро%', 'Синхронизация')
Заменяет все вхождения подстроки str2 в строке str1 подстрокой str3.
-- Вернет 'Десинхронизация' SELECT REPLACE('Синхронизация', 'Синхр', 'Десинхр')
Повторяет i раз строку str.
-- Вернет 'aBaBaBaBaB' SELECT REPLICATE('aB', 5)
Выводит строку str в обратном порядке.
-- Вернет 'яицазинорхниС' SELECT REVERSE('Синхронизация')
Возвращает четырехсимвольный код soundex, используемый для определения похожести двух строк. Работает только для символов ASCII.
Возвращает строку пробелов длиной, указанной в параметре length. Аналог REPLICATE(‘ ‘, length).
Преобразовывает заданное выражение с плавающей точкой f в строку, где len — длина строки, включая десятичную точку, знак, цифры и пробелы (по умолчанию равно 10), а d — число разрядов дробной части, которые нужно возвратить.
-- Вернет '3.14' SELECT STR (3.1415, 4, 2)
Удаляет из строки str1 length-символов, начиная с позиции a, и вставляет на их место строку str2.
-- Note in a book SELECT STUFF('Notebook', 5, 0,' in a ') -- Handbook SELECT STUFF('Notebook', 1, 4, 'Hand')
Извлекает из строки str, начиная с позиции a, подстроку длиной length.
Системные функции
Системные функции языка Transact-SQL предоставляют обширную информацию об объектах базы данных. Большинство системных функций использует внутренний числовой идентификатор (ID), который присваивается каждому объекту базы данных при его создании. Посредством этого идентификатора система может однозначно идентифицировать каждый объект базы данных.
В следующей таблице приводятся некоторые из наиболее важных системных функций вместе с их кратким описанием:
Преобразовывает выражение w в указанный тип данных type (если это возможно). Аргумент w может быть любым действительным выражением.
-- Вернет 3 SELECT CAST (3.1258 AS INT)
Возвращает первое значение выражения из списка выражений a1, a2, . которое не является значением null.
Возвращает длину столбца col объекта базы данных (таблицы или представления) obj.
-- Вернет 4 SELECT COL_LENGTH ('Employee', 'Id')
Эквивалент функции CAST, но аргументы указываются по-иному. Может применяться с любым типом данных.
Возвращает текущие дату и время.
Возвращает имя текущего пользователя.
Возвращает число байтов, которые занимает выражение z.
-- Этот запрос возвращает длину каждого поля SELECT DATALENGTH(FirstName) FROM Employee
Возвращает 1, если использование значений null в базе данных dbname отвечает требованиям стандарта ANSI SQL.
Возвращает значение выражения expr, если оно не равно NULL; в противном случае возвращается значение value.
Определяет, имеет ли выражение expr действительный числовой тип.
Создает однозначный идентификационный номер ID, состоящий из 16-байтовой двоичной строки, предназначенной для хранения значений типа данных UNIQUEIDENTIFIER.
Создает идентификатор GUID, больший, чем любой другой идентификатор GUID, созданный ранее этой функцией на указанном компьютере. (Эту функцию можно использовать только как значение по умолчанию для столбца.)
Возвращает значение null, если значения выражений expr1 и expr2 одинаковые.
-- Запрос возвращает NULL для проекта, -- у которого Number = 'p1' SELECT NULLIF(Number, 'p1') FROM Project
Возвращает информацию о свойствах сервера базы данных.
Возвращает ID текущего пользователя.
Возвращает идентификатор пользователя username. Если пользователь не указан, то возвращается идентификатор текущего пользователя.
Возвращает имя пользователя с указанным идентификатором id. Если идентификатор не указан, то возвращается имя текущего пользователя.
Функции метаданных
По большому счету, функции метаданных возвращают информацию об указанной базе данных и объектах базы данных. В таблице ниже приводятся некоторые из наиболее важных функций метаданных вместе с их кратким описанием:
Возвращает имя столбца с указанным идентификатором col_id таблицы с идентификатором tab_id.
-- Вернет имя столбца 'LastName' SELECT COL_NAME (OBJECT_ID('Employee'), 3)
Возвращает информацию об указанном столбце.
-- Вернет значение свойства PRECISION -- для столбца Id таблицы Employee SELECT COLUMNPROPERTY (OBJECT_ID('Employee'), 'Id', 'precision')
Возвращает значение свойства property базы данных database.
-- Вернет значение свойства IsNullConcat -- для базы данных SampleDb SELECT DATABASEPROPERTY ('SampleDb', 'IsNullConcat')
Возвращает идентификатор базы данных db_name. Если имя базы данных не указано, то возвращается идентификатор текущей базы данных.
Возвращает имя базы данных, имеющей идентификатор db_id. Если идентификатор не указан, то возвращается имя текущей базы данных.
Возвращает имя индексированного столбца таблицы table. Столбец указывается идентификатором индекса i и позицией no столбца в этом индексе.
Возвращает свойства именованного индекса или статистики для указанного идентификационного номера таблицы, имя индекса или статистики, а также имя свойства.
Возвращает имя объекта базы данных, имеющего идентификатор obj_id.
SELECT OBJECT_NAME(245575913);
Возвращает идентификатор объекта obj_name базы данных.
-- Вернет 245575913 - ID таблицы Employee SELECT OBJECT_ID('Employee')
Возвращает информацию об объектах из текущей базы данных.
Какие функции выполняет язык sql
SQL-функции выполняют произвольный список операторов SQL и возвращают результат последнего запроса в списке. В простом случае (не с множеством) будет возвращена первая строка результата последнего запроса. (Помните, что понятие « первая строка » в наборе результатов с несколькими строками определено точно, только если присутствует ORDER BY .) Если последний запрос вообще не вернёт строки, будет возвращено значение NULL.
Кроме того, можно объявить SQL-функцию как возвращающую множество (то есть, несколько строк), указав в качестве возвращаемого типа функции SETOF некий_тип , либо объявив её с указанием RETURNS TABLE( столбцы ) . В этом случае будут возвращены все строки результата последнего запроса. Подробнее это описывается ниже.
Тело SQL-функции должно представлять собой список SQL-операторов, разделённых точкой с запятой. Точка с запятой после последнего оператора может отсутствовать. Если только функция не объявлена как возвращающая void , последним оператором должен быть SELECT , либо INSERT , UPDATE или DELETE с предложением RETURNING .
Любой набор команд на языке SQL можно скомпоновать вместе и обозначить как функцию. Помимо запросов SELECT , эти команды могут включать запросы, изменяющие данные ( INSERT , UPDATE и DELETE ), а также другие SQL-команды. (В SQL -функциях нельзя использовать команды управления транзакциями, например COMMIT , SAVEPOINT , и некоторые вспомогательные команды, в частности VACUUM .) Однако последней командой должна быть SELECT или команда с предложением RETURNING , возвращающая результат с типом возврата функции. Если же вы хотите определить функцию SQL, выполняющую действия, но не возвращающую полезное значение, вы можете объявить её как возвращающую тип void . Например, эта функция удаляет строки с отрицательным жалованьем из таблицы emp :
CREATE FUNCTION clean_emp() RETURNS void AS ' DELETE FROM emp WHERE salary < 0; ' LANGUAGE SQL; SELECT clean_emp(); clean_emp ----------- (1 row)
Примечание
Прежде чем начинается выполнение команд, разбирается всё тело SQL-функции. Когда SQL-функция содержит команды, модифицирующие системные каталоги (например, CREATE TABLE ), действие таких команд не будет проявляться на стадии анализа последующих команд этой функции. Так, например, команды CREATE TABLE foo (. ); INSERT INTO foo VALUES(. ); не будут работать, как ожидается, если их упаковать в одну SQL-функцию, так как foo не будет существовать к моменту разбору команды INSERT . В подобных ситуациях вместо SQL-функции рекомендуется использовать PL/pgSQL .
Синтаксис команды CREATE FUNCTION требует, чтобы тело функции было записано как строковая константа. Обычно для этого удобнее всего заключать строковую константу в доллары (см. Подраздел 4.1.2.4). Если вы решите использовать обычный синтаксис с заключением строки в апострофы, вам придётся дублировать апострофы ( ' ) и обратную косую черту ( \ ) (предполагается синтаксис спецпоследовательностей) в теле функции (см. Подраздел 4.1.2.1).
39.5.1. Аргументы SQL -функций
К аргументам SQL-функции можно обращаться в теле функции по именам или номерам. Ниже приведены примеры обоих вариантов.
Чтобы использовать имя, объявите аргумент функции как именованный, а затем просто пишите это имя в теле функции. Если имя аргумента совпадает с именем какого-либо столбца в текущей SQL-команде внутри функции, имя столбца будет иметь приоритет. Чтобы всё же перекрыть имя столбца, дополните имя аргумента именем самой функции, то есть запишите его в виде имя_функции . имя_аргумента . (Если и это имя будет конфликтовать с полным именем столбца, снова выиграет имя столбца. Неоднозначности в этом случае вы можете избежать, выбрав другой псевдоним для таблицы в SQL-команде.)
Старый подход с нумерацией позволяет обращаться к аргументам, применяя запись $ n : $1 обозначает первый аргумент, $2 — второй и т. д. Это будет работать и в том случае, если данному аргументу назначено имя.
Если аргумент имеет составной тип, то для обращения к его атрибутам можно использовать запись с точкой, например: аргумент . поле или $1. поле . И опять же, при этом может потребоваться дополнить имя аргумента именем функции, чтобы сделать имя аргумента однозначным.
Аргументы SQL-функции могут использоваться только как значения данных, но не как идентификаторы. Например, это приемлемо:
INSERT INTO mytable VALUES ($1);
а это не будет работать:
INSERT INTO $1 VALUES (42);
Примечание
Возможность обращаться к аргументам SQL-функций по именам появилась в PostgreSQL 9.2. В функциях, которые должны работать со старыми серверами, необходимо применять запись $ n .
39.5.2. Функции SQL с базовыми типами
Простейшая возможная функция SQL не имеет аргументов и просто возвращает базовый тип, например integer :
CREATE FUNCTION one() RETURNS integer AS $$ SELECT 1 AS result; $$ LANGUAGE SQL; -- Альтернативная запись строковой константы: CREATE FUNCTION one() RETURNS integer AS ' SELECT 1 AS result; ' LANGUAGE SQL; SELECT one(); one ----- 1
Заметьте, что мы определили псевдоним столбца в теле функции для её результата (дали ему имя result ), но этот псевдоним не виден снаружи функции. Вследствие этого, столбец результата получил имя one , а не result .
Практически так же легко определяются функции SQL , которые принимают в аргументах базовые типы:
CREATE FUNCTION add_em(x integer, y integer) RETURNS integer AS $$ SELECT x + y; $$ LANGUAGE SQL; SELECT add_em(1, 2) AS answer; answer -------- 3
Мы также можем отказаться от имён аргументов и обращаться к ним по номерам:
CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$ SELECT $1 + $2; $$ LANGUAGE SQL; SELECT add_em(1, 2) AS answer; answer -------- 3
Вот более полезная функция, которую можно использовать, чтобы дебетовать банковский счёт:
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$ UPDATE bank SET balance = balance - debit WHERE accountno = tf1.accountno; SELECT 1; $$ LANGUAGE SQL;
Пользователь может выполнить эту функцию, чтобы дебетовать счёт 17 на 100 долларов, так:
SELECT tf1(17, 100.0);
В этом примере мы выбрали имя accountno для первого аргумента, но это же имя имеет столбец в таблице bank . В команде UPDATE имя accountno относится к столбцу bank.accountno , так для обращения к аргументу нужно записать tf1.accountno . Конечно, мы могли бы избежать этого, выбрав другое имя для аргумента.
На практике обычно желательно получать от функции более полезный результат, чем константу 1, поэтому более реалистично такое определение:
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$ UPDATE bank SET balance = balance - debit WHERE accountno = tf1.accountno; SELECT balance FROM bank WHERE accountno = tf1.accountno; $$ LANGUAGE SQL;
Эта функция изменяет баланс и возвращает полученное значение. То же самое можно сделать в одной команде, применив RETURNING :
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$ UPDATE bank SET balance = balance - debit WHERE accountno = tf1.accountno RETURNING balance; $$ LANGUAGE SQL;
Функция SQL должна возвращать в точности объявленный тип результата. Для этого может потребоваться добавить явное приведение. Например, предположим, что мы захотели изменить возвращаемый тип в предыдущей функции на float8 . Этот вариант не будет работать:
CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$ SELECT $1 + $2; $$ LANGUAGE SQL;
несмотря на то, что в других контекстах Postgres Pro выполнил бы неявное приведение для преобразования integer в float8 . Правильный вариант выглядит так:
CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$ SELECT ($1 + $2)::float8; $$ LANGUAGE SQL;
39.5.3. Функции SQL со сложными типами
В функциях с аргументами составных типов мы должны указывать не только, какой аргумент, но и какой атрибут (поле) этого аргумента нам нужен. Например, предположим, что emp — таблица, содержащая данные работников, и это же имя составного типа, представляющего каждую строку таблицы. Следующая функция double_salary вычисляет, каким было бы чьё-либо жалование в случае увеличения вдвое:
CREATE TABLE emp ( name text, salary numeric, age integer, cubicle point ); INSERT INTO emp VALUES ('Bill', 4200, 45, '(2,1)'); CREATE FUNCTION double_salary(emp) RETURNS numeric AS $$ SELECT $1.salary * 2 AS salary; $$ LANGUAGE SQL; SELECT name, double_salary(emp.*) AS dream FROM emp WHERE emp.cubicle ~= point '(2,1)'; name | dream ------+------- Bill | 8400
Обратите внимание на запись $1.salary позволяющую выбрать одно поле из значения строки аргумента. Также заметьте, что в вызывающей команде SELECT указание имя_таблицы .* выбирает всю текущую строку таблицы как составное значение. На строку таблицы можно сослаться и просто по имени таблицы, например так:
SELECT name, double_salary(emp) AS dream FROM emp WHERE emp.cubicle ~= point '(2,1)';
Однако это использование считается устаревшим, так как провоцирует путаницу. (Подробнее эти две записи составных значений строки таблицы описаны в Подразделе 8.16.5.)
Иногда бывает удобно образовать составное значение аргумента на лету. Это позволяет сделать конструкция ROW . Например, так можно изменить данные, передаваемые функции:
SELECT name, double_salary(ROW(name, salary*1.1, age, cubicle)) AS dream FROM emp;
Также возможно создать функцию, возвращающую составной тип. Например, эта функция возвращает одну строку emp :
CREATE FUNCTION new_emp() RETURNS emp AS $$ SELECT text 'None' AS name, 1000.0 AS salary, 25 AS age, point '(2,2)' AS cubicle; $$ LANGUAGE SQL;
В этом примере мы задали для каждого атрибута постоянное значение, но вместо этих констант можно подставить любые вычисления.
Учтите два важных требования относительно определения функции:
Порядок в списке выборки внутреннего запроса должен в точности совпадать с порядком следования столбцов в таблице, связанной с составным типом. (Имена столбцов, как показывает пример выше, для системы значения не имеют.)
Необходимо сделать так, чтобы каждое выражение имело тот же тип, что и соответствующий столбец составного типа, при необходимости добавив приведение. В противном случае вы получите такие ошибки:
ERROR: function declared to return emp returns varchar instead of text at column 1
Ту же функцию можно определить другим способом:
CREATE FUNCTION new_emp() RETURNS emp AS $$ SELECT ROW('None', 1000.0, 25, '(2,2)')::emp; $$ LANGUAGE SQL;
Здесь мы записали SELECT , который возвращает один столбец нужного составного типа. В данной ситуации этот вариант на самом деле не лучше, но в некоторых случаях он может быть удобной альтернативой — например, если нам нужно вычислить результат, вызывая другую функцию, которая возвращает нужное составное значение. Этот вариант полезен и в случае, когда мы хотим написать функцию, которая возвращает не обычный составной тип, а домен, определённый поверх составного типа; тогда она в любом случае должна определяться как возвращающая единственный столбец, так как никаким другим образом получить значение именно доменного типа нельзя.
Мы можем вызывать эту функцию напрямую, либо указав её в выражении значения:
SELECT new_emp(); new_emp -------------------------- (None,1000.0,25,"(2,2)")
либо обратившись к ней, как к табличной функции:
SELECT * FROM new_emp(); name | salary | age | cubicle ------+--------+-----+--------- None | 1000.0 | 25 | (2,2)
Второй способ более подробно описан в Подразделе 39.5.7.
Когда используется функция, возвращающая составной тип, может возникнуть желание получить из её результата только одно поле (атрибут). Это можно сделать, применяя такую запись:
SELECT (new_emp()).name; name ------ None
Дополнительные скобки необходимы во избежание неоднозначности при разборе запроса. Если вы попытаетесь выполнить запрос без них, вы получите ошибку:
SELECT new_emp().name; ERROR: syntax error at or near "." LINE 1: SELECT new_emp().name; ^
(ОШИБКА: синтаксическая ошибка (примерное положение: "."))
Функциональную запись также можно использовать и для извлечения атрибутов:
SELECT name(new_emp()); name ------ None
Как рассказывалось в Подразделе 8.16.5, запись с указанием поля и функциональная запись являются равнозначными.
Ещё один вариант использования функции, возвращающей составной тип, заключается в передаче её результата другой функции, которая принимает этот тип строки на вход:
CREATE FUNCTION getname(emp) RETURNS text AS $$ SELECT $1.name; $$ LANGUAGE SQL; SELECT getname(new_emp()); getname --------- None (1 row)
39.5.4. Функции SQL с выходными параметрами
Альтернативный способ описать результаты функции — определить её с выходными параметрами, как в этом примере:
CREATE FUNCTION add_em (IN x int, IN y int, OUT sum int) AS 'SELECT x + y' LANGUAGE SQL; SELECT add_em(3,7); add_em -------- 10 (1 row)
Это по сути не отличается от версии add_em , показанной в Подразделе 39.5.2. Действительная ценность выходных параметров в том, что они позволяют удобным способом определить функции, возвращающие несколько столбцов. Например:
CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int) AS 'SELECT x + y, x * y' LANGUAGE SQL; SELECT * FROM sum_n_product(11,42); sum | product -----+--------- 53 | 462 (1 row)
Фактически здесь мы определили анонимный составной тип для результата функции. Показанный выше пример даёт тот же конечный результат, что и команды:
CREATE TYPE sum_prod AS (sum int, product int); CREATE FUNCTION sum_n_product (int, int) RETURNS sum_prod AS 'SELECT $1 + $2, $1 * $2' LANGUAGE SQL;
Но предыдущий вариант зачастую удобнее, так как он не требует отдельно заниматься определением составного типа. Заметьте, что имена, назначаемые выходным параметрам, не просто декоративные, а определяют имена столбцов анонимного составного типа. (Если вы опустите имя выходного параметра, система выберет имя сама.)
Заметьте, что выходные параметры не включаются в список аргументов при вызове такой функции из SQL. Это объясняется тем, что Postgres Pro определяет сигнатуру вызова функции, рассматривая только входные параметры. Это также значит, что при таких операциях, как удаление функции, в ссылках на функцию учитываются только типы входных параметров. Таким образом, удалить эту конкретную функцию можно любой из этих команд:
DROP FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int); DROP FUNCTION sum_n_product (int, int);
Параметры функции могут быть объявлены как IN (по умолчанию), OUT , INOUT или VARIADIC . Параметр INOUT действует как входной (является частью списка аргументов при вызове) и как выходной (часть типа записи результата). Параметры VARIADIC являются входными, но обрабатывается специальным образом, как описано далее.
39.5.5. Функции SQL с переменным числом аргументов
Функции SQL могут быть объявлены как принимающие переменное число аргументов, с условием, что все « необязательные » аргументы имеют один тип данных. Необязательные аргументы будут переданы такой функции в виде массива. Для этого в объявлении функции последний параметр помечается как VARIADIC ; при этом он должен иметь тип массива. Например:
CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$ SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i); $$ LANGUAGE SQL; SELECT mleast(10, -1, 5, 4.4); mleast -------- -1 (1 row)
По сути, все фактические аргументы, начиная с позиции VARIADIC , собираются в одномерный массив, как если бы вы написали
SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- это не будет работать
На самом деле так вызвать эту функцию нельзя, или, по крайней мере, это не будет соответствовать определению функции. Параметру VARIADIC соответствуют одно или несколько вхождений типа его элемента, но не его собственного типа.
Но иногда бывает полезно передать функции с переменными параметрами уже подготовленный массив; особенно когда одна функция с переменными параметрами хочет передавать свой массив параметров другой. Также это более безопасный способ вызывать такую функцию, существующую в схеме, где могут создавать объекты недоверенные пользователи; см. Раздел 10.3. Это можно сделать, добавив VARIADIC в вызов:
SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
Это предотвращает разворачивание переменного множества параметров функции в базовый тип, что позволяет сопоставить с ним значение типа массива. VARIADIC можно добавить только к последнему фактическому аргументу вызова функции.
Также указание VARIADIC даёт единственную возможность передать пустой массив функции с переменными параметрами, например, так:
SELECT mleast(VARIADIC ARRAY[]::numeric[]);
Простой вызов SELECT mleast() не будет работать, так как переменным параметрам должен соответствовать минимум один фактический аргумент. (Можно определить вторую функцию с таким же именем mleast , но без параметров, если вы хотите выполнять такие вызовы.)
Элементы массива, создаваемые из переменных параметров, считаются не имеющими собственных имён. Это означает, что передать функции с переменными параметрами именованные аргументы нельзя (см. Раздел 4.3), если только при вызове не добавлено VARIADIC . Например, этот вариант будет работать:
SELECT mleast(VARIADIC arr => ARRAY[10, -1, 5, 4.4]);
А эти варианты нет:
SELECT mleast(arr => 10); SELECT mleast(arr => ARRAY[10, -1, 5, 4.4]);
39.5.6. Функции SQL со значениями аргументов по умолчанию
Функции могут быть объявлены со значениями по умолчанию для некоторых или всех входных аргументов. Значения по умолчанию подставляются, когда функция вызывается с недостаточным количеством фактических аргументов. Так как аргументы можно опускать только с конца списка фактических аргументов, все параметры после параметра со значением по умолчанию также получат значения по умолчанию. (Хотя запись с именованными аргументами могла бы ослабить это ограничение, оно всё же остаётся в силе, чтобы позиционные ссылки на аргументы оставались действительными.) Независимо от того, используете вы эту возможность или нет, она требует осторожности при вызове функций в базах данных, где одни пользователи не доверяют другим; см. Раздел 10.3.
CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3) RETURNS int LANGUAGE SQL AS $$ SELECT $1 + $2 + $3; $$; SELECT foo(10, 20, 30); foo ----- 60 (1 row) SELECT foo(10, 20); foo ----- 33 (1 row) SELECT foo(10); foo ----- 15 (1 row) SELECT foo(); -- не работает из-за отсутствия значения по умолчанию для первого аргумента ERROR: function foo() does not exist
(ОШИБКА: функция foo() не существует) Вместо ключевого слова DEFAULT можно использовать знак = .
39.5.7. Функции SQL , порождающие таблицы
Все функции SQL можно использовать в предложении FROM запросов, но наиболее полезно это для функций, возвращающих составные типы. Если функция объявлена как возвращающая базовый тип, она возвращает таблицу с одним столбцом. Если же функция объявлена как возвращающая составной тип, она возвращает таблицу со столбцами для каждого атрибута составного типа.
CREATE TABLE foo (fooid int, foosubid int, fooname text); INSERT INTO foo VALUES (1, 1, 'Joe'); INSERT INTO foo VALUES (1, 2, 'Ed'); INSERT INTO foo VALUES (2, 1, 'Mary'); CREATE FUNCTION getfoo(int) RETURNS foo AS $$ SELECT * FROM foo WHERE fooid = $1; $$ LANGUAGE SQL; SELECT *, upper(fooname) FROM getfoo(1) AS t1; fooid | foosubid | fooname | upper -------+----------+---------+------- 1 | 1 | Joe | JOE (1 row)
Как показывает этот пример, мы можем работать со столбцами результата функции так же, как если бы это были столбцы обычной таблицы.
Заметьте, что мы получаем из данной функции только одну строку. Это объясняется тем, что мы не использовали указание SETOF . Оно описывается в следующем разделе.
39.5.8. Функции SQL , возвращающие множества
Когда SQL-функция объявляется как возвращающая SETOF некий_тип , конечный запрос функции выполняется до завершения и каждая строка выводится как элемент результирующего множества.
Это обычно используется, когда функция вызывается в предложении FROM . В этом случае каждая строка, возвращаемая функцией, становится строкой таблицы, появляющейся в запросе. Например, в предположении, что таблица foo имеет то же содержимое, что и раньше, мы выполняем:
CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$ SELECT * FROM foo WHERE fooid = $1; $$ LANGUAGE SQL; SELECT * FROM getfoo(1) AS t1;
Тогда в ответ мы получим:
fooid | foosubid | fooname -------+----------+--------- 1 | 1 | Joe 1 | 2 | Ed (2 rows)
Также возможно выдать несколько строк со столбцами, определяемыми выходными параметрами, следующим образом:
CREATE TABLE tab (y int, z int); INSERT INTO tab VALUES (1, 2), (3, 4), (5, 6), (7, 8); CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int) RETURNS SETOF record AS $$ SELECT $1 + tab.y, $1 * tab.y FROM tab; $$ LANGUAGE SQL; SELECT * FROM sum_n_product_with_tab(10); sum | product -----+--------- 11 | 10 13 | 30 15 | 50 17 | 70 (4 rows)
Здесь ключевая особенность заключается в записи RETURNS SETOF record , показывающей, что функция возвращает множество строк вместо одной. Если существует только один выходной параметр, укажите тип этого параметра вместо record .
Часто бывает полезно сконструировать результат запроса, вызывая функцию, возвращающую множество, несколько раз, передавая при каждом вызове параметры из очередных строк таблицы или подзапроса. Для этого рекомендуется применить ключевое слово LATERAL , описываемое в Подразделе 7.2.1.5. Ниже приведён пример использования функции, возвращающей множество, для перечисления элементов древовидной структуры:
SELECT * FROM nodes; name | parent -----------+-------- Top | Child1 | Top Child2 | Top Child3 | Top SubChild1 | Child1 SubChild2 | Child1 (6 rows) CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$ SELECT name FROM nodes WHERE parent = $1 $$ LANGUAGE SQL STABLE; SELECT * FROM listchildren('Top'); listchildren -------------- Child1 Child2 Child3 (3 rows) SELECT name, child FROM nodes, LATERAL listchildren(name) AS child; name | child --------+----------- Top | Child1 Top | Child2 Top | Child3 Child1 | SubChild1 Child1 | SubChild2 (5 rows)
В этом примере не делается ничего такого, что мы не могли бы сделать, применив простое соединение, но для более сложных вычислений возможность поместить некоторую логику в функцию может быть весьма удобной.
Функции, возвращающие множества, могут также вызываться в списке выборки запроса. Для каждой строки, которая генерируется самим запросом, вызывается функция, возвращающая множество, и для каждого элемента набора её результатов генерируется отдельная строка. Предыдущий пример можно было бы также переписать с применением запросов следующим образом:
SELECT listchildren('Top'); listchildren -------------- Child1 Child2 Child3 (3 rows) SELECT name, listchildren(name) FROM nodes; name | listchildren --------+-------------- Top | Child1 Top | Child2 Top | Child3 Child1 | SubChild1 Child1 | SubChild2 (5 rows)
Заметьте, что в последней команде SELECT для Child2 , Child3 и т. д. строки не выдаются. Это происходит потому, что listchildren возвращает пустое множество для этих аргументов, так что строки результата не генерируются. Это же поведение мы получаем при внутреннем соединении с результатом функции с применением LATERAL .
Поведение Postgres Pro с функциями, возвращающими множества, в списке выборки запроса практически не отличается от поведения с такими функциями, помещёнными в предложение LATERAL FROM . Например, запрос:
SELECT x, generate_series(1,5) AS g FROM tab;
SELECT x, g FROM tab, LATERAL generate_series(1,5) AS g;
Он мог быть полностью идентичным, но в данном конкретном примере планировщик может решить перенести g во внешнюю сторону соединения, так как g не имеет фактической зависимости по времени вычисления от tab . Такое решение привело бы к изменению порядка строк. Функции, возвращающие множества, в списке выборки всегда вычисляются так, как они вычислялись бы внутри соединения с вложенным циклом с остальным предложением FROM , так что эти функции выполняются до завершения прежде чем начинается рассмотрение следующей строки из предложения FROM .
Если в списке выборки запроса используются несколько функций, возвращающих запросы, они вычисляются примерно так же, как если бы они были помещены в один элемент LATERAL ROWS FROM( . ) предложения FROM . Для каждой строки из нижележащего запроса выдаётся строка с первым результатом каждой функции, а затем строка со вторым результатом и так далее. Если какие-либо из функций, возвращающих множества, выдают меньше результатов, чем другие, то вместо недостающих данных подставляются значения NULL, так что общее число строк, выдаваемых для одной нижележащей строки, равно числу строк, которое выдаёт функция с наибольшим количеством строк в возвращаемом множестве. Таким образом, функции, возвращающие множества, выполняются совместно, пока все их множества не будут исчерпаны, а затем выполнение продолжается со следующей нижележащей строкой.
Функции, возвращающие множества, могут быть вложенными в списке выборки, но это не допускается в элементах предложения FROM . В таких случаях каждый уровень вложенности обрабатывается отдельно, как если бы это был отдельный элемент LATERAL ROWS FROM( . ) . Например, в
SELECT srf1(srf2(x), srf3(y)), srf4(srf5(z)) FROM tab;
возвращающие множества функции srf2 , srf3 и srf5 будут выполняться совместно для каждой строки tab , а затем srf1 и srf4 будут совместно применяться к каждой строке, произведённой нижними функциями.
Функции, возвращающие множества, нельзя использовать в конструкциях, вычисляемых по условию, например, CASE или COALESCE . Например, рассмотрите запрос
SELECT x, CASE WHEN x > 0 THEN generate_series(1, 5) ELSE 0 END FROM tab;
Может показаться, что он должен выдать пять экземпляров входных строк, в которых x > 0 , и по одному экземпляру остальных строк; но на деле, так как generate_series(1, 5) будет выполняться в неявном элементе LATERAL FROM до того, как выражение CASE вообще будет рассматриваться, должно было бы выдаваться пять экземпляров абсолютно всех выходных строк. Во избежание путаницы в таких случаях выдаётся ошибка при разборе запроса.
Примечание
Если последняя команда функции — INSERT , UPDATE или DELETE с RETURNING , эта команда будет всегда выполняться до завершения, даже если функция не объявлена с указанием SETOF или вызывающий запрос не выбирает все строки результата. Все дополнительные строки, выданные предложением RETURNING , просто игнорируются, но соответствующие изменения в таблице всё равно произойдут (и будут завершены до выхода из функции).
Примечание
В Postgres Pro до версии 10 при помещении нескольких функций, возвращающих множества, в один список выборки поведение было не очень разумным, если они возвращали не одинаковое число строк. В таких случаях число выходных строк равнялось наименьшему общему множителю количеств строк, возвращаемых этими функциями. Также и вложенные функции, возвращающие множества, работали не так, как описано выше; у такой функции мог быть максимум один аргумент, возвращающий множество, и каждая вложенность вычислялась независимо. Кроме того, ранее допускалось и условное выполнение (вычисление таких функций внутри CASE и т. п.), что ещё больше всё усложняло. При написании запросов, которые должны работать и со старыми версиями Postgres Pro , рекомендуется использовать синтаксис LATERAL , так как это гарантирует одинаковый результат с разными версиями. Если в вашем запросе используется условное вычисление функции, возвращающей множество, его можно исправить, переместив проверку условия в специально созданную функцию, возвращающую множество. Например:
SELECT x, CASE WHEN y > 0 THEN generate_series(1, z) ELSE 5 END FROM tab;
можно заменить на
CREATE FUNCTION case_generate_series(cond bool, start int, fin int, els int) RETURNS SETOF int AS $$ BEGIN IF cond THEN RETURN QUERY SELECT generate_series(start, fin); ELSE RETURN QUERY SELECT els; END IF; END$$ LANGUAGE plpgsql; SELECT x, case_generate_series(y > 0, 1, z, 5) FROM tab;
Это будет работать одинаково во всех версиях Postgres Pro .
39.5.9. Функции SQL , возвращающие таблицы ( TABLE )
Есть ещё один способ объявить функцию, возвращающую множества, — использовать синтаксис RETURNS TABLE( столбцы ) . Это равнозначно использованию одного или нескольких параметров OUT с объявлением функции как возвращающей SETOF record (или SETOF тип единственного параметра, если это применимо). Этот синтаксис описан в последних версиях стандарта SQL, так что этот вариант может быть более портируемым, чем SETOF .
Например, предыдущий пример с суммой и произведением можно также переписать так:
CREATE FUNCTION sum_n_product_with_tab (x int) RETURNS TABLE(sum int, product int) AS $$ SELECT $1 + tab.y, $1 * tab.y FROM tab; $$ LANGUAGE SQL;
Запись RETURNS TABLE не позволяет явно указывать OUT и INOUT для параметров — все выходные столбцы необходимо записать в списке TABLE .
39.5.10. Полиморфные функции SQL
Функции SQL могут быть объявлены как принимающие и возвращающие полиморфные типы anyelement , anyarray , anynonarray , anyenum и anyrange . За более подробным объяснением полиморфизма функций обратитесь к Подразделу 39.2.5. В следующем примере полиморфная функция make_array создаёт массив из двух элементов произвольных типов:
CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$ SELECT ARRAY[$1, $2]; $$ LANGUAGE SQL; SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray; intarray | textarray ----------+----------- | (1 row)
Обратите внимание на приведение типа 'a'::text , определяющее, что аргумент имеет тип text . Оно необходимо, если аргумент задаётся просто строковой константой, так как иначе он будет воспринят как имеющий тип unknown , а массив типов unknown является недопустимым. Без этого приведения вы получите такую ошибку:
ERROR: could not determine polymorphic type because input has type "unknown"
(ОШИБКА: не удалось определить полиморфный тип, так как входные аргументы имеют тип "unknown")
Функция с полиморфными аргументами может иметь фиксированный тип результата, однако обратное не допускается. Например:
CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$ SELECT $1 > $2; $$ LANGUAGE SQL; SELECT is_greater(1, 2); is_greater ------------ f (1 row) CREATE FUNCTION invalid_func() RETURNS anyelement AS $$ SELECT 1; $$ LANGUAGE SQL; ERROR: cannot determine result data type DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
(ОШИБКА: не удалось определить тип результата; ПОДРОБНОСТИ: Функция, возвращающая полиморфный тип, должна иметь минимум один полиморфный аргумент.)
Полиморфизм можно применять и с функциями, имеющими выходные аргументы. Например:
CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE SQL; SELECT * FROM dup(22); f2 | f3 ----+--------- 22 | (1 row)
Полиморфизм также можно применять с функциями с переменными параметрами. Например:
CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$ SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i); $$ LANGUAGE SQL; SELECT anyleast(10, -1, 5, 4); anyleast ---------- -1 (1 row) SELECT anyleast('abc'::text, 'def'); anyleast ---------- abc (1 row) CREATE FUNCTION concat_values(text, VARIADIC anyarray) RETURNS text AS $$ SELECT array_to_string($2, $1); $$ LANGUAGE SQL; SELECT concat_values('|', 1, 4, 2); concat_values --------------- 1|4|2 (1 row)
39.5.11. Функции SQL с правилами сортировки
Когда функция SQL принимает один или несколько параметров сортируемых типов данных, правило сортировки определяется при каждом вызове функции, в зависимости от правил сортировки, связанных с фактическими аргументами, как описано в Разделе 23.2. Если правило сортировки определено успешно (то есть не возникло конфликтов между неявно установленными правилами сортировки аргументов), оно неявно назначается для всех сортируемых параметров. Выбранное правило будет определять поведение операций, связанных с сортировкой, в данной функции. Например, для показанной выше функции anyleast , результат
SELECT anyleast('abc'::text, 'ABC');
будет зависеть от правила сортировки по умолчанию, заданного в базе данных. С локалью C результатом будет строка ABC , но со многими другими локалями это будет abc . Нужное правило сортировки можно установить принудительно, добавив предложение COLLATE к одному из аргументов функции, например:
SELECT anyleast('abc'::text, 'ABC' COLLATE "C");
С другой стороны, если вы хотите, чтобы функция работала с определённым правилом сортировки, вне зависимости от того, с каким она была вызвана, вставьте предложения COLLATE где требуется в определении функции. Эта версия anyleast всегда будет сравнивать строки по правилам локали en_US :
CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$ SELECT min($1[i] COLLATE "en_US") FROM generate_subscripts($1, 1) g(i); $$ LANGUAGE SQL;
Но заметьте, что при попытке применить правило к несортируемому типу данных, возникнет ошибка.
Если для фактических аргументов не удаётся определить общее правило сортировки, функция SQL считает, что им назначено правило сортировки по умолчанию для их типа данных (обычно это то же правило сортировки, что определено по умолчанию для базы данных, но оно может быть и другим для параметров доменных типов).
Поведение сортируемых параметров можно воспринимать как ограниченную форму полиморфизма, применимую только к текстовым типам данных.
| Пред. | Наверх | След. |
| 39.4. Пользовательские процедуры | Начало | 39.6. Перегрузка функций |
Функции в SQL
Функции в SQL подобны любым другим запросам языка в том смысле, что они производят действия с данными и возвращают результат в качестве своего значения. В СУБД обычно имеются три основных класса функций: встроенные, определяемые пользователем и хранимые (процедурные).
Встроенными являются функции, предопределенные в языке SQL конкретной СУБД. В SQL определено множество встроенных функций различных категорий [18, 19, 21, 22, 23].
Эти функции делятся на основные группы:
- 1) скалярные функции;
- 2) агрегатные функции;
- 3) функции для списка значений;
- 4) функции выбора вариантов;
- 5) оконные (аналитические) функции.
Скалярные функции (их еще называют однострочными) обрабатывают одиночное значение и возвращают также одно значение. Скалярные функции разрешается использовать везде, где допускается применение выражений.
Скалярные функции бывают следующих категорий:
- • строковые функции, которые выполняют определенные действия над строками и возвращают строковые или числовые значения;
- • числовые функции, которые возвращают числовые значения на основании заданных в аргументе значений того же типа;
- • функции времени и даты, которые выполняют различные действия над входными значениями времени и даты и возвращают строковое, числовое значения или значение в формате даты и времени;
- • функция преобразования типа.
Агрегатные функции используются для получения обобщающих значений. Они, в отличие от скалярных функций, оперируют значениями столбцов множества строк. К агрегатным функциям относятся такие функции, как SUM, вычисляющая итог, МАХ и MIN, возвращающие соответственно наибольшее и наименьшее значения, AVG, вычисляющая среднее значение, и COUNT, рассчитывающая количество значений в столбце.
Функции для списка значений используются для обработки значений, заданных в списке аргументов. В Firebird такие функции представлены тремя функциями — MAXVALUE, MINVALUE и LIST. Функции MAXVALUE и MINVALUE в чем-то похожи на агрегатные функции МАХ и MIN, однако выбирают максимальное и минимальное значения не из множества значений строк в одном столбце, а из значений, заданных в списке аргументов. Функция LIST объединяет обрабатываемые значения в единую строку.
Особое место среди встроенных скалярных функций языка SQL занимают функции выбора вариантов, которые являются разновидностью CASE-выражений. В качестве функций выбора вариантов используются функции COALESCE, NULLIF, DECODE. Возвращаемый этими функциями результат меняется в зависимости от значения, которое обрабатывается функцией.
Оконные (аналитические) функции предназначены для статистического анализа данных в готовой выборке. Это довольно мощный и удобный инструмент, который присутствует во многих СУБД.
Существует несколько типов аналитических функций:
- 1) агрегатные функции;
- 2) функции ранжирования;
- 3) навигационные функции.
Оконные функции будут подробно рассмотрены в параграфе 3.2.7 после изучения секций GROUP BY и ORDER BY, так как для понимания алгоритмов работы аналитических функций необходимо понимание работы запросов с группировкой и сортировкой.
Функции, определяемые пользователем (User Defined Function, UDF) являются вспомогательными программами, написанными на языке программирования, таком как С, C+ + или Pascal, и компилированными как совместно используемые двоичные библиотеки — DLL [13]. Внешние функции можно использовать в выражениях, так же как и встроенные функции SQL. Они могут возвращать значения для переменных или выражений SQL в хранимых процедурах и триггерах.
Система управления БД Firebird содержит две готовые к использованию библиотеки UDF: Ib_Udf и FbUdf. Firebird загружает UDF из библиотек, находящихся в каталоге Udf каталога инсталляции или в других каталогах, указанных в параметре UdfAccess в файле конфигурации Firebird. Когда пользовательская функция написана, компилирована и инсталлирована в соответствующий каталог на сервере, она должна быть объявлена для БД, чтобы ее можно было использовать как функцию SQL. Для объявления внешней функции используется оператор DECLARE EXTERNAL FUNCTION. Можно объявить функцию также с использованием любого интерактивного инструмента SQL или скрипта. После того как функция будет объявлена для любой БД на сервере, содержащая ее библиотека будет загружаться при первом же обращении приложения к любой функции, включенной в эту библиотеку. Необходимо объявить каждую функцию, которую нужно использовать, для каждой БД, которая будет использоваться.
Хранимые функции являются подпрограммами, написанными на процедурном языке СУБД. Такие функции обычно создаются для реализации бизнес-логики и являются самостоятельным объектом БД. Хранимые функции будут рассмотрены в параграфе 6.3 при изучении процедурных языков.
Рассмотрим подробнее различные виды встроенных функций СУБД Firebird 3.0.
3.2.3.2. Скалярные функции
Строковые функции. Эти функции используют в качестве аргумента строку символов, а в качестве результата возвращают также символьную строку или числовое значение.
Для выделения подстроки указанной длины из строкового выражения, начиная с заданной позиции, используется функция SUBSTRING. Функция имеет формат
SUBSTRING(cTpoKOBoe_Bbipa>FROM позиция [FOR длина])
Здесь позиция — позиция, начиная с которой выполняется выделение, например 1 для первого символа в строке; длина — количество выделяемых символов. Если длина не задана, то выделение производится до конца исходной строки. Задание отрицательного значения длины приводит к тому, что символы отсчитываются от конца строки, а не от начала. Задание длины, превышающей по абсолютному значению длину строки, приводит к тому, что функция возвращает NULL.
Например, для вывода номеров лицевых счетов абонентов и первых трех символов их ФИО (рис. 3.21) можно использовать запрос
SELECT A.AccountCD, SUBSTRING(A.Fio FROM 1 FOR 3) AS "Fio3"
Следует учесть, что тип значений столбца Fio3 будет такой же, как и тип первого аргумента (строковое выражение, из которого выделяются символы), поскольку ширина столбца Fio таблицы Abonent равна 30 [VARCHAR(30)], то столбец Fio3 будет иметь также ширину 30, а не 3.
Стоит отметить, что в качестве аргументов SUBSTRING могут использоваться подзапросы, возвращающие единственное значение ( ).

Рис. 3.21. Результат работы функции SUBSTRING
В качестве аргументов позиция и длина в SUBSTRING можно использовать и выражения (в том числе со скалярными функциями, возвращающими числовой результат).
Следует отдельно отметить, что в Firebird 3.0 существует возможность использования функции SUBSTRING с регулярными выражениями [21]. При этом функция имеет следующий формат:
В таком формате функция SUBSTRING возвращает часть строки из строкового выражения, соответствующую регулярному выражению в предложении SIMILAR. Возвращаемое значение соответствует части регулярного выражения, и для этого значения истинно выражение
Регулярные шаблоны составляются в соответствии с правилами, описанными в подпараграфе 3.2.2.10. Если соответствие не установлено, то возвращается значение NULL.
Например, запрос вида
SELECT SUBSTRING('abcabc' SIMILAR ESCAPE '#')
вернет строку 'bcab', а запрос вида
SELECT SUBSTRING('abcabc' SIMILAR ’#"(abc)*#'" ESCAPE '#’)
вернет строку вида 'abcabc'.
Для вывода строки в обратной последовательности (начиная с конца) используется функция REVERSE. Функция имеет формат
Например, для вывода информации об абонентах, имеющих инициалы Е.В. (рис. 3.22), можно использовать запрос
SELECT * FROM Abonent
WHERE REVERSE(Fio) STARTING WITH REVERSE('E. B.');

Puc. 3.22. Результат работы функции REVERSE
Функции LEFT и RIGHT используются для выделения нужного количества символов соответственно из начала или конца определенной строки и имеют формат
Р16НТ(строковое_выражение, длина) где строковое_выражение — выражение, из которого будут выделяться символы; длина — количество выделяемых символов в начале (для LEFT) или в конце (для RIGHT) строки.
Например, предыдущий запрос, использующий функцию REVERSE, можно реализовать с помощью функции RIGHT таким образом:
SELECT * FROM Abonent WHERE RIGHT(Fio, 5) = 'E. B.’;
Результат выполнения запроса будет совпадать с результатом, представленным на рис. 3.22.
Например, для вывода Fio абонентов, проживающих на улице с кодом 3, и первых четырех цифр из номеров их лицевых счетов можно использовать запрос
SELECT Fio, LEFT(AccountCD, 4) FROM Abonent WHERE StreetCD = 3; Результат выполнения запроса представлен на рис. 3.23.

Рис. 3.23. Результат работы функции LEFT
Существует ряд функций для замены части исходной строки на другую последовательность символов.
Функция OVERLAY заменяет в исходной строке подстроку, начинающуюся с номера позиция и имеющую размер длина, на значение строки для замены. Функция имеет формат
OVERLAY( исходная_строка PLACING строка_для_замены FROM позиция [ FOR длина ] )
где исходная_строка и строка_для_замены могут представлять собой строковое выражение.
Если длина не указана, то по умолчанию принимается длина в символах строки для замены.
Например, если требуется вывести номера телефонов абонентов с заменой первых двух цифр на '66', можно использовать запрос
SELECT OVERLAY(Phone PLACING '66' FROM 1) FROM Abonent;
Функция OVERLAY эквивалентна следующему выражению с использованием функции SUBSTRING:
SUBSTRING(ncxoflHafl_CTpoKa FROM 1 FOR (позиция - 1))
|| SUBSTRING(ncxoflHafl_CTpoKa FROM (позиция + длина))
Функция REPLACE заменяет все вхождения подстроки в строковое выражение на указанную строку для замены. Она имеет формат:
REPLACE(строковое_выражение, подстрока, строка_для_замены)
Например, для замены в названии неисправностей слова «плиты» на словосочетание «газовой плиты» (рис. 3.24) можно выполнить запрос
SELECT REPLACE(FailureNM, 'плиты', 'газовой плиты') FROM Disrepair;

Рис. 3.24. Результат работы функции REPLACE
Функция TRIM возвращает строку аргумента, удаляя символы (по умолчанию — пробелы) из начала и (или) конца строки. Для вызова функции в стандарте SQL используется синтаксис
TRIM( [ [LEADING | TRAILING | BOTH ] [ удаляемая_подстрока ]
LEADING указывает на то, что нужно удалить указанную подстроку из начала строки, TRAILING — из конца строки, BOTH — из начала и конца строки.
При использовании функции следует учитывать следующее:
- • если LEADING, TRAILING или BOTH не указаны, то принимается BOTH по умолчанию;
- • если удаляемая подстрока не определена, то по умолчанию принимается пробел;
если LEADING, или TRAILING, или BOTH и (или) удаляемая подстрока указаны, то после слова FROM должно быть обязательно указано строковое_выражение, из которого удаляются символы;
• указание строки, из которой удаляются символы, после слова FROM не может быть использовано самостоятельно (если LEADING, TRAILING, BOTH и удаляемая_подстрока не указаны).
Например, для вывода данных из таблицы Street, указывая название улицы без слова «УЛИЦА» (рис. 3.25), можно использовать запрос
SELECT StreetCD, TRIM(BOTH 'УЛИЦА' FROM StreetNM) AS "Str_Name FROM Street;

Puc. 3.25. Результат работы функции TRIM
Для вывода лицевых счетов абонентов без нулей слева (рис. 3.26) можно использовать запрос
SELECT TRIM(LEADING '0' FROM AccountCD) AS AccountCD, Fio FROM Abonent;

Puc. 3.26. Результат работы функции TRIM (удаление символов слева)
Существуют функции LPAD и RPAD, которые дополняют строку аргумента слева (LPAD) или справа (RPAD) указанной последовательностью символов: строка_заполнитель до заданного размера длина. Если строка_заполнитель не указана, для дополнения используется пробел.
Строка_заполнитель обрезается, когда результирующая строка достигает заданной длины. Функции имеют следующий формат:
LPAD( строковое_выражение, длина [, строка_заполнитель ] )
RPAD( строковое_выражение, длина [, строка_заполнитель ] )
Например, требуется вывести номера лицевых счетов абонентов и Fio, дополненные справа знаком звездочки (*) до длины 20 символов (рис. 3.27). Запрос будет выглядеть таким образом:
SELECT AccountCD, RPAD (Fio, 20,'*') FROM Abonent;

Puc. 3.27. Результат работы функции RPAD
Существуют функции, которые выполняют преобразования между регистрами символов. Функция UPPER преобразует все символы строки в верхний регистр и имеет синтаксис
где — преобразуемый столбец, переменная или выражение строкового типа.
Если набор символов и последовательность сортировки поддерживают преобразование в верхний регистр (например, WIN1251), то функция возвращает строку, в которой все символы преобразованы в верхний регистр.
Строка имеет ту же длину, что и входное . Для наборов символов, не поддерживающих преобразование в верхний регистр (в частности, кодировка по умолчанию NONE), функция возвращает неизменное входное значение. Следующий запрос выводит названия услуг заглавными буквами (рис. 3.28):
SELECT UPPER(ServiceNM) FROM Services;

Рис. 3.28. Результат работы функции UPPER
Противоположной является функция LOWER, которая преобразует все символы строки в нижний регистр:
где — преобразуемый столбец, переменная или выражение строкового типа.
Существует функция для определения символа по известному коду. Функция имеют синтаксис
Например, запрос на определение символа с кодом 82 SELECT ASCII_CHAR(82) FROM RDB$DATABASE;
Код должен лежать в диапазоне от 0 до 255.
Функция ASCII_CHAR может использоваться для вставки управляющих символов в символьные строки.
Рассмотренные выше строковые функции возвращают результат в виде строки символов. Существует также ряд строковых функций, которые в качестве результата возвращают числовое значение.
Среди них функции, обратные предыдущим описанным, для определения кода символа (крайнеголевого) в указанной строке. В Firebird используется функция ASCII_VAL. Функция имеет следующий синтаксис:
Например, запрос, определяющий код символа «R»:
SELECT ASCII_VAL('Ready') FROM RDB$DATABASE;
Функция вернет NULL, если указана пустая строка.
Для определения позиции первого вхождения заданной подстроки в строку можно использовать функцию POSITION:
POSITION!(подстрока IN строковое_выражение
| подстрока, строковое_выражение [, позиция])>
где позиция — номер символа, с которого начинается поиск подстроки, например 5 для пятого символа в строке. Отсчёт начинается с 1. Аргумент позиция задаёт позицию в строке, с которой начинается поиск подстроки, тем самым игнорируя любые вхождения подстроки в строку до этой позиции.
Функция возвращает ноль, если подстрока отсутствует внутри строки. Например, для вывода номеров лицевых счетов и ФИО тех абонентов, у которых в фамилиях вторая буква у (рис. 3.29), можно использовать запрос
SELECT AccountCD, Fio FROM Abonent WHERE POSITION('у’ IN Fio) = 2;
SELECT AccountCD, Fio FROM Abonent WHERE POSITION ('y', Fio) = 2;

Puc. 3.29. Результат работы функции POSITION
В любой СУБД существуют функции для определения размера строки. В Firebird для этого применяются функции BIT_LENGTH, CHAR[ACTER]_ LENGTH, OCTETJLENGTH.
Функция BITJLENGTH возвращает длину строки в битах, функция CHAR[ACTER]_LENGTH — в символах, а функция OCTET_LENGTH — в байтах.
Эти три функции имеют одинаковый синтаксис
Например, вывести названия услуг и длины названий в символах и в битах (рис. 3.30) можно с помощью запроса
SELECT ServiceNM, CHAR_LENGTH(ServiceNM), BIT_LENGTH(ServiceNM)

Puc. 3.30. Результаты работы функций CHAR_LENGTH и BIT_LENGTH
Проверить, что тип VARCHAR предусматривает автоматическое отбрасывание символов пробела (рис. 3.31), можно с помощью следующего запроса, использующего функции CHAR_LENGTH и TRIM:
SELECT FIRST 3 FailureNM AS "FailureNM",
CHAR_LENGTH(FailureNM), CHAR_LENGTH(TRIM(FailureNM)) FROM Disrepair;

Puc. 3.31. Результат работы функции CHAR_LENGTH
Во втором столбце выводятся длины значений столбца FailureNM в символах, а в третьем — длины значений этого же столбца, но с удаленными символами пробелов. Можно заметить, что значения во втором и третьем столбцах совпадают, так как тип столбца FailureNM — VARCHAR(50) и символы пробелов автоматически отбрасываются.
Рассмотрим пример запроса, позволяющего подсчитать, сколько раз символ или подстрока встречается в заданной строке. Для подсчета количества букв 'и' в ФИО абонентов можно использовать такой запрос:
/CHAR_LENGTH('и') FROM Abonent;
В этом запросе при вычитании из длины строки длины строки без подстроки получается разница, которая соответствует количеству букв «и» в ФИО. Последней операцией является деление на длину строки поиска. Эта операция необходима, если длина искомой строки более одного символа.
Для преобразования значения уникального идентификатора (UUID) между двоичным и строковым видами в Firebird используются функции CHAR_TO_UUID и UUID_TO_CHAR. Функция CHAR_TO_UUID преобразует строковое (CHAR(32) кодировки ASCII) представление UUID (вида ХХХХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХХХХХХХХ) в двоичное (CHARQ6) кодировки OCTETS), более оптимизированное для хранения. Функция UUID_TO_CHAR выполняет обратное преобразование, т. е. преобразует двоичный UUID, полученный обычно с помощью функции GENJJUID (см. табл. 3.4), в строковый. Функции имеют формат
Например, можно использовать следующие запросы:
SELECT CHAR_TO_UUID ('93S19227-8D50-4E47-81AA-8F6678C096A1') FROM RDB$DATABASE;
SELECT UUID_TO_CHAR(GEN_UUID()) FROM RDB$DATABASE;
В заключение описания строковых функций рассмотрим две системные функции Firebird, позволяющие получать информацию о параметрах БД, о текущем подключении и транзакции, а также параметры действия триггеров DDL: RDB$GET_CONTEXT и RDB$SET_CONTEXT [21, 22].
Синтаксис функции RDB$GET_CONTEXT RDB$GET_CONTEXT( 1 1 , ' ')
— регистрозависимая строка длиной до 80 символов.
Функция RDB$GET_CONTEXT возвращает значение контекстной переменной из пространств имен SYSTEM, DDL_TRIGGER, USER_ SESSION, USER_TRANSACTION. Пространство имен и имя переменной — это строковые выражения, которые чувствительны к регистру и должны быть отличны от NULL.
Если в указанном пространстве есть запрашиваемая переменная, то будет возвращено ее значение типа VARCHAR(255). Если указано несуществующее пространство имен или указано пространство имен SYSTEM или DDL_TRIGGER, но запрашиваемая переменная в нем не существует, то будет сгенерирована ошибка. Если запрашивается несуществующая переменная в другом пространстве имен, то функция вернет NULL.
Пространство имен SYSTEM доступно только для чтения и содержит предустановленные переменные, которые приведены в табл. 3.2.
Контекстные переменные в пространстве имен SYSTEM
Возвращает текущую версию SQL-сервера. Например: '2.5.0'
Сетевой протокол, используемый клиентом в текущем соединении с БД. Может иметь значения 'TCPv4', 'WNET', 'XNET' или NULL
Сетевой адрес клиента. Для протокола TCPv4 содержит IP-адрес. Для протокола XNET содержит ID локального процесса. Для всех остальных протоколов содержит NULL
Имя БД, в зависимости от подключения возвращает алиас или путь к файлу, указанные в строке подключения к БД
Уровень изоляции текущей транзакции. Может иметь значения 'READ, COMMITTED', 'SNAPSHOT' или 'CONSISTENCY'
Номер текущей транзакции (значение идентично переменной CURRENT_TRANSACTION)
Номер текущего подключения (значение идентично переменной CURRENT_CONNECTION)
Текущий пользователь (значение идентично переменной CURRENT JJSER)
Текущая роль (значение идентично переменной CURRENT_ ROLE)
Например, получить текущую версию SQL-сервера Firebird можно с помощью запроса
SELECT RDB$GET_CONTEXT ('SYSTEM', 'ENGINE_VERSION')
Пространство имен DDL_TRIGGER доступно только для чтения и содержит предустановленные переменные, которые приведены в табл. 3.3. Значения переменных доступны только в процессе работы триггера DDL (триггеры DDL будут подробно рассмотрены в подпараграфе 6.6.2).
Контекстные переменные в пространстве имен DDL_TRIGGER
Тип события: CREATE, ALTER или DROP
Тип объекта БД (TABLE, VIEW и т. д.)
Событие DDL в виде EVENT_TYPE | | " | | OBJECT_TYPE
Наименование объекта метаданных
Пространства имен USER_SESSION и USER_TRANSACTION изначально пустые и не содержат переменных. Пользователь может создавать переменные и задавать им значения с помощью функции RDB$SET_CONTEXT(), а потом получить их значение с помощью функции RDB$GET_CONTEXT().
Функция RDB$SET_CONTEXT имеет синтаксис
где ::= < USER_SESSION | USER_TRANSACTION>; — чувствительная к регистру строка длиной до 80 символов; — любое выражение, приводимое к типу VARCHAR (255).
Функция RDB$SET_CONTEXT создает, задает или удаляет значение контекстной переменной в пространствах имен USER_SESSION и USER_TRANSACTION. При этом следует помнить, что максимальное количество переменных в любом контексте (транзакции или соединения) равно 1000. Переменные пространства имен USER_SESSION привязаны к текущему соединению, а переменные пространства имен USER_TRANSACTION существуют в контексте транзакции, в которой были созданы. Когда транзакция завершается, все созданные в ней переменные уничтожаются.
Функция RDB$SET_CONTEXT возвращает результат типа INTEGER: 1, если переменная уже существует, и 0, если переменная еще не была создана. Чтобы удалить переменную, необходимо установить ее значение в NULL.
Установим, например, в пределах текущего подключения значение пользовательской переменной MyVar в значение 86, а затем получим значение данной переменной. Запросы для выполнения указанных действий будут выглядеть следующим образом:
Функции RDB$SET_CONTEXT() и RDB$GET_CONTEXT() являются встроенными, но представляют собой подобие «продекларированных UDF-функций», хотя и находятся не во внешней библиотеке. Это сделано для возможности использовать их как UDF-функции — например, вызывать как процедуры в PSQL:
New.UserAddr := RDB$GET_CONTEXT ('SYSTEM', 'CLIENT_ADDRESS');
С обычными встроенными функциями это невозможно.
Числовые функции. Эти функции возвращают числовые значения на основании значений того же типа, заданных в аргументе. Числовые функции используются для обработки данных, а также в условиях поиска. Стандартные числовые функции СУБД перечислены в табл. 3.4 [12, 13, 21] .
Случайное число от 0 до 1
Язык SQL. Функции и основные возможности
1. Возможность в запросе группировать кортежи отношений результата по указанным полям в соответствии с условиями выборки на всю группу целиком. 2. В SQL не обязательно удалять кортежи-дубликаты как в результате, так и в промежуточном кортеже. Результатом выполнения запроса SQL является мультимножественный кортеж. В СУБД System R были реализованы все теоретикомножественные операторы. В System R реализованы 4 оператора манипулирования данными и 2 оператора создания представления. Также дополнительные операторы позволяют вносить изменения в структуру таблицы.
Определение ограничения целостности и тригеров
В System R средства поддержки целостности БД – это asserHon (исключения) – логическое выражение, вып-е над всей БД, ложность которого говорила о нецелостности БД. Ограничения целостности делились на 2 класса: 1. проверяемый после выполнения оператора манипулирования(транзакции) 2. по требованию специального оператора INFORCE INTEGRITY Типы предикатов отличались для этих групп. В System R был впервые реализован механизм триггеров, которые служили для автоматического поддержания целостности БД. При определении триггера задавалось условие его применения, и при условии true триггер срабатывал.
Представление БД
27 Для определения представлений используется аппарат операторов выборки.
Определение управляющих структур
Определение управляющих структур представляет индексы и связи. В SQL появилось четкое представление того, как представлять индексы и связи. (UNIQUE;CLUSTERING)
Авторизация доступа к отношениям и их полям
К любому отношению и к любому столбцу м.б. обеспечить авторизируемый доступ. С каждой транзакцией неявно связывался оператор пользователя. Основные операторы: • GRANT – «Разрешить • REVOKE – «собрать» права
Точки сохранения и отката транзакций. Встроенный SQL
Встроенный SQL – это специальные операторы, поддерживающие возможность выполнения SQL операторов в традиционных языках программирования. ESQL/C – язык С с возможностью написания запросов. Основная проблема со встраиванием – проблема сопряженности декларированных и процедурных языков программирования. Проблема доступа к кортежу в процедурных языках была решена путем использования механизма «курсора», который представляет собой указатель на кортеж. Основные операторы над курсорами: OPEN CURSOR FETCH CURSOR CLOSE CURSOR
Динамический SQL
Механизм, позволяющий выполнять операторы SQL на этапе компиляции. 1. Ограничение уникальности (не более одного первичного ключа) 2. Ограничение по ссылкам (удовлетворяется, если для каждой корректной ссылки есть объект, на который она ссылается) 3. Проверочное ограничение (специфицирует условие, которому должна удовлетворять каждая строка таблицы)
Определение представлений.
VIEW Механизм представлений view является мощным средством SQL, позволяющим скрыть от пользователя реальную структуру БД, за счёт по сути хранимого в базе запроса с именованными столбцами. Для пользования view (или представления )ничем не отличается от базовой таблицы. Представление реально накладывается на таблицу всякий раз в момент выполнения. Стандарт SQL-89, механизм представлений определяется следующим образом:
Определение привилегий.
В стандарт SQL-89 раздача привилегий возможна только при определении таблицы. Пользователь, получивший привилегии от другого пользователя может их передать дальше только при определении схемы. Привилегии определяются следующим образом: < privilege definition >::= GRAND
29
Средства манипулирования данными.
Структура запросов.
