Семинар 8. Хранимые функции, процедуры, триггеры
Содержание:
Теоретическая справка
Хранимый код — объект базы данных, представляющий собой набор SQL-инструкций (и не только их), который компилируется один раз и хранится на сервере, т.е. функции, процедуры, триггеры.
Зачем нужен хранимый код?
- Позволяет декомпозировать логику, скрывая внутреннюю реализацию;
- Поддерживает функции безопасности и целостности данных, обработку исключений.
1. Функции
PostgreSQL предоставляет большое количество встроенных функций. Например:
-- Функция повтора строки
-- repeat (text, int) → text
SELECT repeat('Pg', 4); -- 'PgPgPgPg'
-- Экспонента
-- exp (double precision) → double precision
SELECT exp(1.0); -- 2.7182818284590452
-- Функция перевода строки в верхний регистр
-- upper (text) → text
SELECT upper('hello world'); -- HELLO WORLD
Весь список можно посмотреть здесь.
Собственные функции можно писать не только на SQL, но и на других ЯП, об этом в разделе Интеграция с другими ЯП.
Они определяются командой CREATE FUNCTION
или CREATE OR REPLACE FUNCTION
:
CREATE FUNCTION somefunc(int, text) RETURNS int
AS 'тело функции'
LANGUAGE [SQL | plpgsql | plpython | ...];
Обращение к аргументам функции осуществляется через $1
, $2
и т.д или через алиасы самих аргументов, если они указаны.
Простейший вариант функции на SQL:
-- Определение функции
CREATE FUNCTION add(int, int) RETURNS int
AS 'SELECT $1 + $2;'
LANGUAGE SQL;
-- Запрос
SELECT add(1, 3); -- 4
---- Запрос к таблице
-- Создаём таблицу
CREATE TABLE test_t(a int, b int);
-- Добавляем значения
INSERT INTO test_t(a, b)
VALUES
(3, 10),
(5, 20);
-- Запрос с использованием определенной функции
SELECT add(a, b)
FROM test_t;
--- Ответ
-- |add|
-- | 13|
-- | 25|
NOTE:
Если писать
CREATE FUNCTION
, тело функции представляет собой просто текстовую строку. Часто для написания тела функции удобнее заключать эту строку в доллары, а не в обычные апострофы. Если не применять заключение в доллары, все апострофы или обратные косые черты в теле функции придётся экранировать, дублируя их.
1.1. Функции SQL со значениями аргументов по умолчанию
Значения аргументов по умолчанию определяются ключевым словом DEFAULT
:
CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3) RETURNS int AS $$
SELECT a + b + c;
$$ LANGUAGE SQL;
SELECT foo(10, 20, 30); -- 60
SELECT foo(10, 20); -- 33
SELECT foo(10); -- 15
Вместо ключевого слова DEFAULT
можно использовать знак =
.
1.2. Функции SQL, возвращающие нескольких значений
Функция, возвращающая запись с несколькими выходными параметрами (составной тип):
CREATE FUNCTION dup(in int, out f1 int, out f2 text) AS $$
-- Здесь CAST(выражение AS тип) приводит $1 к текстовому типу
SELECT $1, CAST($1 AS text) || ' is text'
$$ LANGUAGE SQL;
-- Запрос
SELECT * FROM dup(42);
-- Ответ
-- | f1 | f2 |
-- | 42 | 42 is text |
То же самое можно сделать более развёрнуто, явно объявив составной тип:
CREATE TYPE dup_result AS (f1 int, f2 text);
CREATE FUNCTION dup(int) RETURNS dup_result
AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
LANGUAGE SQL;
SELECT * FROM dup(42);
1.3. Функции SQL, возвращающие таблицы
Ещё один способ вернуть несколько столбцов — использовать синтаксис RETURNS TABLE
:
CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text)
AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
LANGUAGE SQL;
Однако запись RETURNS TABLE
не позволяет явно указывать OUT
и INOUT
для параметров — все выходные столбцы необходимо записать в списке TABLE
.
1.4. Функции SQL, модифицирующие таблицы
Помимо запросов SELECT, команды внутри функции могут включать запросы, изменяющие данные (INSERT, UPDATE, DELETE, MERGE и т.д.):
CREATE TABLE bank(account_id int, balance numeric);
CREATE FUNCTION debit_bank_acc(account_id int, debit numeric) RETURNS numeric AS $$
UPDATE bank
SET balance = balance - debit
WHERE account_id = debit_bank_acc.account_id;
SELECT balance
FROM bank
WHERE account_id = debit_bank_acc.account_id
$$ LANGUAGE SQL;
1.5. Модификаторы функций
Каждая функция имеет свою степень/категорию изменчивости: VOLATILE
, STABLE
, IMMUTABLE
.
Для лучшей эксплуатации оптимизатора следует использовать ту допустимую категорию, которая дает больше гарантий о своем поведении.
1. VOLATILE (Изменчивая)
Значение по умолчанию в CREATE FUNCTION
.
Функция не дает никаких гарантий о своем поведении: поведение может быть недетерминированно при одинаковых аргументах, модифицировать внутренне состояние базы данных, и т.д.
Оптимизатор не делает никаких предположений касаемо поведения такой функции.
Запросы, использующие VOLATILE
функции будут пересчитывать значение функции на каждой применяемой строке таблицы.
Примеры функций: random()
, currval()
, timeofday()
.
SELECT random() = random(); -- false
2. STABLE (Стабильная)
Функция дает гарантию, что не модифицирует внутреннее состояние базы и гарантирует детерминированное поведение на одних и тех же аргументах в рамках одного запроса.
Поэтому данная степень изменчивости позволяет оптимизировать множественные вызовы путем кеширования.
В частности, такие функции можно использовать для поиска по индексу (для VOLATILE
такое запрещено).
Примеры функций: now()
, current_setting()
, current_timestamp()
.
-- Не меняются в рамках одного запроса/транзакции
SELECT now() = now(); -- true
-- Но:
SELECT now(); -- 2025-01-12 15:21:51.240537 +00:00
SELECT now(); -- 2025-01-12 15:21:51.567379 +00:00
3. IMMUTABLE (Постоянная)
Функция дает те же гарантии, что и STABLE
, но снимает ограничение на рамки одного запроса.
Поэтому оптимизатор может предварительно “прогреть кэш” на константных аргументах.
Примеры функций: abs(x)
, lower(text)
.
Другие модификаторы
- CALLED ON NULL INPUT - функция вызывается даже, если входной параметр равен
NULL
; - RETURNS NULL ON NULL INPUT - функция возвращает
NULL
, если хотя бы один из её аргументов равенNULL
; - STRICT - функция должна вернуть ровно одну строку, или произойдёт ошибка во время выполнения;
- LEAKPROOF - функция не раскрывает информацию через ошибки (для безопасности).
Пример:
CREATE OR REPLACE FUNCTION calculate_circle_area(radius double precision) RETURNS double precision AS $$
SELECT PI() * radius * radius;
$$ LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
SELECT calculate_circle_area(1); -- 3.14159265358979
2. Интеграция с другими ЯП
PostgreSQL позволяет разрабатывать собственный хранимый код и на других языках, отличных от SQL (на C также, но в рамках курса не рассматриваем). Эти другие языки обычно называются процедурными языками (PL, Procedural Languages). В рамках курса мы рассмотрим:
2.1. PL/pgSQL
PL/pgSQL — язык программирования, используемый для написания хранимого кода PostgreSQL. С помощью данного расширения можно писать выполняемые блоки, функции и особый объект баз данных – триггеры.
2.1.1. Структура функций
Тела функций на PL/pgSQL состоят из блоков. Структура блока (в […] указаны опциональные команды):
[ <<метка>> ]
[ DECLARE объявления ]
BEGIN
операторы
END [ метка ];
Пример:
CREATE FUNCTION add_one (int) RETURNS int AS $$
BEGIN
RETURN $1 + 1;
END;
$$ LANGUAGE plpgsql;
DECLARE
определяет внутренние переменные функции:
CREATE FUNCTION add_one (int) RETURNS int AS $$
<<block1>> -- Метка блока
DECLARE
delta int = 1;
BEGIN
RETURN $1 + delta;
END;
$$ LANGUAGE plpgsql;
Метка используется, когда нужно дополнить имена переменных, объявленных в этом блоке.
Явно это будет показано в примере с вложенными блоками ниже.
Если метка указана после END
, то она должна совпадать с меткой в начале блока.
2.1.2. Вложенные блоки кода
Вложенные блоки используются для логической группировки нескольких операторов или локализации области действия переменных для группы операторов.
Во время выполнения вложенного блока переменные, объявленные в нём, скрывают переменные внешних блоков с такими же именами. Чтобы получить доступ к внешним переменным, нужно дополнить их имена меткой блока.
CREATE FUNCTION somefunc() RETURNS int AS $$
<< outerblock >>
DECLARE
quantity int = 30;
BEGIN
RAISE NOTICE 'Сейчас quantity = %', quantity; -- Выводится 30
quantity = 50;
-- Вложенный блок
DECLARE
quantity int = 80;
BEGIN
RAISE NOTICE 'Сейчас quantity = %', quantity; -- Выводится 80
RAISE NOTICE 'Во внешнем блоке quantity = %', outerblock.quantity; -- Выводится 50
END;
RAISE NOTICE 'Сейчас quantity = %', quantity; -- Выводится 50
RETURN quantity;
END;
$$ LANGUAGE plpgsql;
NOTES:
Каждое логическое действие, будь то
DECLARE
,RETURN
,RAISE
, … (BEGIN
таковым не является), в блоке должно завершаться символом;
. Каждый вложенный блок, также должен иметь точку с запятой послеEND
, как показано выше. Однако финальныйEND
, завершающий тело функции, не требует;
.Ключевые слова не чувствительны к регистру символов. Как и в обычных SQL-командах, идентификаторы неявно преобразуются к нижнему регистру, если они не взяты в двойные кавычки.
2.1.3. Блочные комментарии
Блочный комментарий начинается с /*
и завершается */
.
Они также могут быть вложенными.
CREATE FUNCTION add_ten (int) RETURNS int AS $$
-- Комментарий 1
/*
Комментарий 2
Комментарий 2
*/
/*
Комментарий 3
Комментарий 3
/*
Вложенный комментарий 3
*/
*/
BEGIN
RETURN $1 + 10;
END;
$$ LANGUAGE plpgsql;
2.1.4. Операторы PL/pgSQL
Важно понимать, что внутри блоков функции или процедуры в PL/pgSQL можно использовать различные операторы и структуры такие, как условные операторы, циклы, динамические запросы и многое другое.
Условные операторы
PL/pgSQL поддерживает классические IF ... THEN
и CASE ... WHEN
выражения по аналогии с обычным SQL.
CREATE OR REPLACE FUNCTION classify_number(number numeric) RETURNS text AS $$
DECLARE
result text;
BEGIN
IF number = 0 THEN
result = 'zero';
ELSIF number > 0 THEN
result = 'positive';
ELSIF number < 0 THEN
result = 'negative';
ELSE
result = 'NULL';
END IF;
RETURN result;
END;
$$ LANGUAGE plpgsql;
Выполнение динамических запросов
Часто требуется динамически формировать команды внутри функций на PL/pgSQL, то есть такие команды, в которых при каждом выполнении могут использоваться разные таблицы или типы данных.
Для исполнения динамических команд предусмотрен оператор EXECUTE
:
EXECUTE строка-команды
[ INTO цель ]
[ USING выражение [, ... ] ];
Строка-команды — это команды типа text
, которую нужно выполнить.
Необязательная цель — это то, куда будут помещены результаты команды.
Необязательные выражения в USING
формируют значения, которые будут вставлены в команду.
В тексте команды можно использовать значения параметров, ссылки на параметры обозначаются как $1
, $2
и т.д.
Эти символы указывают на значения, находящиеся в команде USING
.
Пример:
DO $$
DECLARE
c int; -- Переменная для хранения результата
checked_user text = 'user123';
checked_date date = '2001-09-11';
BEGIN
-- Выполнение динамического запроса
EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
INTO c
USING checked_user, checked_date;
RAISE NOTICE 'Count: %', c;
END;
$$;
DO
здесь выполняет анонимный блок кода, то есть не сохраняет его в базе как функцию, а сразу исполняет один раз и выводит в консоль.
Циклы
Синтаксис цикла:
[<<метка>>]
LOOP
операторы
END LOOP [ метка ];
EXIT
:
EXIT [ метка ] [WHEN логическое-выражение];
При наличии WHEN
цикл прекращается, только если логическое-выражение истинно.
В противном случае управление переходит к оператору, следующему за EXIT
.
CONTINUE
:
CONTINUE [ метка ] [WHEN логическое-выражение];
При наличии WHEN
следующая итерация цикла начинается только тогда, когда логическое-выражение истинно.
В противном случае управление переходит к оператору, следующему за CONTINUE
.
WHILE
:
DO $$
DECLARE
counter int = 1;
BEGIN
WHILE counter < 1000 LOOP
counter = counter * 2;
RAISE NOTICE 'Counter: %', counter;
END LOOP;
END;
$$;
- Цикл
FOR
по целым числам:
DO $$
DECLARE
start_value int = 20;
end_value int = 0;
step int = 2;
i int;
BEGIN
-- Цикл с start_value по end_value с шагом step в обратном порядке
FOR i IN REVERSE start_value..end_value BY step LOOP
RAISE NOTICE 'Current value: %', i;
END LOOP;
END;
$$;
- Цикл
FOR
по результатам запроса:
DO $$
DECLARE
rec RECORD; -- Переменная для хранения текущей строки
BEGIN
FOR rec IN SELECT id, name FROM mytable WHERE active = TRUE LOOP
RAISE NOTICE 'ID: %, Name: %', rec.id, rec.name;
END LOOP;
END;
$$;
- Цикл
FOREACH
по массиву:
DO $$
DECLARE
numbers int[] = ARRAY[10, 20, 30, 40, 50];
num int;
BEGIN
FOREACH num IN ARRAY numbers LOOP
RAISE NOTICE 'Number: %', num;
END LOOP;
END;
$$;
NOTE про транзакции
Важно не путать использование BEGIN/END
для группировки операторов в PL/pgSQL с одноимёнными SQL-командами для управления транзакциями.
BEGIN/END
в PL/pgSQL служат только для группировки предложений.
Они не начинают и не заканчивают транзакции.
Про управление транзакциями в PL/pgSQL можно почитать в документации.
Кроме того, блок с командой EXCEPTION
по сути создаёт вложенную транзакцию, которую можно отменить, не затрагивая внешнюю транзакцию.
2.2. PL/Python
Как установить PL/Python
Прежде чем что-то использовать, это надо установить.
Если вы используете контейнер с postgres
поверх apline
, то скачайте пакеты:
apk add --no-cache --virtual .plpython3-deps --repository http://nl.alpinelinux.org/alpine/edge/testing \
postgresql-plpython3 \
&& ln -s /usr/lib/postgresql/plpython3.so /usr/local/lib/postgresql/plpython3.so \
&& ln -s /usr/share/postgresql/extension/plpython3u.control /usr/local/share/postgresql/extension/plpython3u.control \
&& ln -s /usr/share/postgresql/extension/plpython3u--1.0.sql /usr/local/share/postgresql/extension/plpython3u--1.0.sql \
&& ln -s /usr/share/postgresql/extension/plpython3u--unpackaged--1.0.sql /usr/local/share/postgresql/extension/plpython3u--unpackaged--1.0.sql
После установите PL/Python в определённую базу данных, выполнив команду:
CREATE EXTENSION plpython3u; -- Будем использовать Python3
Функции на PL/Python объявляются стандартным образом с помощью команды CREATE FUNCTION
:
CREATE FUNCTION имя_функции (аргументы) RETURNS возвращаемый_тип
AS $$
# Тело функции на PL/Python
$$ LANGUAGE plpython3u;
Тело функции содержит просто скрипт на языке Python.
Когда вызывается функция, её аргументы передаются в виде элементов списка args.
Результат из кода Python возвращается обычным способом, командой return
или yield
(в случае функции, возвращающей множество).
Если возвращаемое значение не определено, Python возвращает None
. Исполнитель PL/Python преобразует None
языка Python в значение NULL
языка SQL.
Например, функцию, возвращающее большее из двух целых чисел, можно определить так:
CREATE FUNCTION pymax (a int, b int) RETURNS int
AS $$
if a > b:
return a
return b
$$ LANGUAGE plpython3u;
Проблема переопределения аргументов функций
Значения аргументов задаются в глобальных переменных. Согласно правилам видимости в Python, тонким следствием этого является то, что переменной аргумента нельзя присвоить внутри функции выражение, включающее имя самой этой переменной, если только эта переменная не объявлена глобальной в текущем блоке. Например, следующий код не будет работать:
CREATE FUNCTION pystrip(x text) RETURNS text
AS $$
x = x.strip() # ошибка
return x
$$ LANGUAGE plpython3u;
Добавив оператор global
, это можно исправить:
CREATE FUNCTION pystrip(x text) RETURNS text
AS $$
global x
x = x.strip() # теперь всё в порядке
return x
$$ LANGUAGE plpython3u;
Анонимные функции
PL/Python также поддерживает анонимные блоки кода, которые выполняются оператором DO
:
DO $$
# Код на PL/Python
$$ LANGUAGE plpython3u;
3. Процедуры
Процедура — объект базы данных, подобный функции, но не возвращающий значение. Она обладает следующими особенностями по сравнению с функциями:
- Процедура определяется командой
CREATE PROCEDURE
. - В
CREATE PROCEDURE
отсутствует командаRETURNS
. Однако процедуры могут выдавать данные в вызывающий код через выходные параметры. - Вызывается отдельно командой
CALL
. - Процедура может фиксировать или откатывать транзакции во время её выполнения (а затем автоматически начинать новую транзакцию), если вызывающая команда
CALL
находится не в блоке транзакции. - Некоторые модификаторы функций (например,
STRICT
) неприменимы к процедурам. Эти модификаторы влияют на вызов функций в запросах и не имеют отношения к процедурам.
Пример процедуры вставки записей в таблицу:
CREATE OR REPLACE PROCEDURE insert_acc_record(account_id int, balance numeric) AS $$
INSERT INTO bank VALUES (account_id, balance);
$$ LANGUAGE SQL;
CALL insert_acc_record(1, 5.0);
CALL insert_acc_record(2, 0.0);
Пример процедуры удаления записей из таблицы:
CREATE OR REPLACE PROCEDURE clean_negative_balance() AS $$
DELETE FROM bank
WHERE balance < 0;
$$ LANGUAGE SQL;
CALL clean_negative_balance();
Процедуры с выходными параметрами
Процедуры также поддерживают выходные параметры, но несколько иначе, чем функции.
В командах CALL
выходные параметры должны быть включены в список аргументов.
Обычно принято писать на их месте NULL
.
Например, описанную ранее операцию списания средств с банковского счета можно записать следующим образом:
CREATE OR REPLACE PROCEDURE debit_bank_acc(account_id int, debit numeric, OUT new_balance numeric) AS $$
UPDATE bank
SET balance = balance - debit
WHERE account_id = debit_bank_acc.account_id
RETURNING balance;
$$ LANGUAGE SQL;
CALL debit_bank_acc(1, 3, NULL);
4. Триггеры
Триггер — хранимая процедура особого типа, которую пользователь не вызывает непосредственно, а она сама исполняется при по модификации данных:
- Добавлением
INSERT
; - Удалением
DELETE
строки в заданной таблице; - Изменением
UPDATE
данных в определённом столбце заданной таблицы реляционной базы данных.
Триггеры могут выполняться до/после/вместо основного действия и для всей строки/всего выражения модификации данных:
Алгоритм создания триггера:
- Создание специальной хранимой функции, которая:
- Не принимает ничего на вход;
- Возвращает тип
TRIGGER
; - Может использовать специальные переменные вида
TG_variable
.
- Создание непосредственно триггера, который запускает эту функцию из п.1.
Синтаксис создания триггера:
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER имя { BEFORE | AFTER | INSTEAD OF } { событие [ OR ... ] }
ON имя_таблицы
[ FROM ссылающаяся_таблица ]
[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
[ REFERENCING { { OLD | NEW } TABLE [ AS ] имя_переходного_отношения } [ ... ] ]
[ FOR [ EACH ] { ROW | STATEMENT } ]
[ WHEN ( условие ) ]
EXECUTE { FUNCTION | PROCEDURE } имя_функции ( аргументы )
-- Здесь допускаются события:
INSERT
UPDATE [ OF имя_столбца [, ... ] ]
DELETE
TRUNCATE
Специальные переменные вида TG_variable
:
NEW
– переменная типаRECORD
, содержащая новую строкуUPDATE
/INSERT
операций.NULL
дляDELETE
;OLD
– переменная типаRECORD
, содержащая старую строкуUPDATE
/DELETE
операций.NULL
дляINSERT
;TG_WHEN
– переменная типаTEXT
, указывающая на время срабатывания триггера:BEFORE
,AFTER
,INSTEAD OF
;TG_LEVEL
– переменная типаTEXT
, соответствующая типу триггера по уровню срабатывания:ROW
,STATEMENT
;TG_OP
– переменная типаTEXT
, соответствующая типу операции, на которую был вызван триггер:INSERT
,DELETE
,UPDATE
,TRUNCATE
;TG_TABLE_NAME
– переменная типаNAME
, соответствующая таблице, которая вызвала срабатывание триггера;TG_TABLE_SCHEMA
– переменная типаNAME
, соответствующая схеме, в которой хранится таблица, вызвавшая срабатывание триггера.
Рассмотрим пример триггера, логгирующий изменения таблицы accounts
в таблицу account_changes
:
CREATE TABLE accounts (
id serial PRIMARY KEY,
username varchar(50) NOT NULL,
balance numeric(10, 2) DEFAULT 0
);
-- Таблица журнала изменений
CREATE TABLE account_changes (
id serial PRIMARY KEY,
account_id int NOT NULL,
changed_at timestamp DEFAULT current_timestamp,
old_data jsonb,
new_data jsonb
);
-- Определение функции log_account_update()
CREATE OR REPLACE FUNCTION log_account_update() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO account_changes (account_id, old_data, new_data)
VALUES (NEW.id, to_jsonb(OLD), to_jsonb(NEW));
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- Создание триггера на таблицу accounts
CREATE OR REPLACE TRIGGER log_update
AFTER UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE FUNCTION log_account_update();
INSERT INTO accounts (username, balance)
VALUES
('bushido_zho', 100.00),
('pozdnyakov', 200.00);
-- Здесь сработает триггер
UPDATE accounts
SET balance = 150.00
WHERE username = 'bushido_zho';
Триггеры создаются вне схем, в привязке к конкретной таблице базы данных. Удаляются тоже с указанием таблицы, на которую триггер создавался:
DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
5. Полезные ссылки
- Создание функций
- Строковые константы, заключённые в доллары
- Обработка ошибок в PL/pgSQL
- Про типы и их взаимную конвертацию в PL/Python
- Обращение к данным базы данных через PL/Python
- Вспомогательные функции в PL/Python
- Создание триггеров
- Обзор механизма работы триггеров
6. Практическое задание
Функции, процедуры:
- Требуется написать функцию, которая будет выводить на экран фразу «Hello, World!»;
- Требуется написать функцию, которая будет переворачивать строку, получаемую на вход;
- Требуется написать функцию, которая будет рассчитывать факториал заданного числа;
- Требуется написать функцию, которая будет прибавлять к дате в формате
YYYY-MM-DD
n дней; - Требуется написать код, который создаст копии всех имеющихся таблиц вашей БД какой-либо схемы, например, добавив к ним суффикс
copy
; - Требуется написать функцию на PL/pgSQL, которая проверяет по возрасту пользователя, является ли он совершеннолетним;
- Требуется написать функцию на PL/pgSQL, которая возвращает возраст пользователя по его имени, если пользователь есть в таблице,
NULL
в противном случае; - Требуется написать функцию, которая считает сумму чисел от 1 до n;
- Требуется написать функцию на PL/Python, которая возводит заданное число в указанную степень;
- Требуется написать функцию на PL/Python, которая проверяет является ли заданная строка палиндромом (читается одинаково слева направо и справа налево);
- Требуется написать функцию на PL/Python, которая считает количество слов в строке;
- Требуется написать процедуру, которая удаляет из таблицы всех пользователей, старше заданного значения;
- Требуется написать процедуру, которая подсчитывает общее количество пользователей в таблице и возвращает результат;
- Требуется написать процедуру, которая добавляет пользователя в таблицу по имени и дате регистрации. Если дата не передана, использовать текущую дату.
Триггеры:
Пусть дана таблица employee
, в которой содержится информация о сотрудниках и их зарплате, а также информация о том, кто и когда в последний раз менял запись (создайте и наполните ее самостоятельно):
- Требуется создать триггер, который при любом добавлении или изменении строки в таблице сохраняет в этой строке информацию о текущем пользователе и отметку времени. Кроме того, он требует, чтобы было указано имя сотрудника и зарплата задавалась положительным числом.
- Требуется создать триггер, который будет записывать все изменения таблицы
employee
в отдельную таблицу логов. Информация, которая должна быть отражена в таблице логов:- Какая операция была совершена;
- Время операции;
- Пользователь, который совершил операцию;
- Значения новых полей.
- Требуется добавить к таблице
employee
полеproject_id
и создать на нее триггер, который будет автоматически обновлять статус проекта в таблицеprojects
. Если к проекту добавлен хотя бы один сотрудник, статус проекта меняется на “Active”. Если все сотрудники удалены из проекта, статус проекта меняется на “Pending”. - Требуется создать триггер, который записывает в таблицу логов все добавления или удаления сотрудников из проектов.
В логах должны храниться:
- Время операции;
- Тип операции (“ADD” или “REMOVE”);
- Сотрудник, который добавлен или удален;
- Проект, в котором произошли изменения.