Использование PL/pgSQL
Обзор
PL/pgSQL — это загружаемый процедурный язык для СУБД ADPG/PostgreSQL.
SQL — язык, который ADPG/PostgreSQL и большинство других реляционных баз данных используют в качестве языка запросов. Каждый оператор SQL должен выполняться сервером базы данных индивидуально. Это означает, что клиентское приложение должно отправлять каждый запрос на сервер базы данных, ждать его обработки, получать и обрабатывать результаты, а затем отправлять дальнейшие запросы на сервер. Все это требует межпроцессного взаимодействия, а также влечет за собой значительную нагрузку на сеть.
С помощью PL/pgSQL можно группировать запросы и вычисления внутри сервера базы данных. Это позволяет использовать преимущества процедурного языка и SQL, существенно сокращая объем передачи данных между клиентом и сервером, что может привести к значительному увеличению производительности по сравнению с приложением без хранимых (stored) функций.
PL/pgSQL имеет следующие преимущества:
-
Позволяет создавать функции, процедуры и триггеры. Функции и хранимые процедуры, разработанные с помощью PL/pgSQL, можно использовать как любые встроенные функции и хранимые процедуры.
-
Предоставляет структуры управления для языка SQL.
-
Позволяет выполнять сложные вычисления.
-
Наследует все определяемые пользователем типы, функции, процедуры и операторы.
-
Может быть определен как доверенный для сервера базы данных PostgreSQL.
-
Прост в использовании.
В ADPG расширение plpgsql
, необходимое для использования PL/pgSQL, предустановлено в базе данных postgres
. При создании новой базы данных устанавливаются расширения, присутствующие в базе данных template1
. По умолчанию в template1
установлено расширение plpgsql
. См. Template Databases.
Структура PL/pgSQL
PL/pgSQL поддерживает анонимные блоки кода, выполняемые с помощью оператора DO.
DO
$$
DECLARE
book_num integer;
BEGIN
SELECT count(*) into book_num FROM book;
RAISE NOTICE 'The number of books: %', book_num;
END;
$$;
Если необходимо повторно использовать код, можно создать функцию или процедуру. Для этого используйте команду CREATE FUNCTION или CREATE PROCEDURE. В следующем примере определяется функция, написанная на PL/pgSQL. LANGUAGE
указывает имя языка, на котором написана функция:
CREATE FUNCTION my_function(integer, text) RETURNS integer
AS 'function body text'
LANGUAGE plpgsql;
Тело функции представляет собой строковый литерал. Часто для записи тела функции используются долларовые кавычки ($$
), а не обычный синтаксис одинарных кавычек (см. Dollar-quoted string constants). Без долларовых кавычек любые одинарные кавычки или обратную косую черту в теле функции необходимо экранировать, удвоив их.
PL/pgSQL имеет блочную структуру. Полный текст тела функции должен быть блоком. Блок определяется следующим образом:
[ <<<метка>>> ]
[ DECLARE
<объявления> ]
BEGIN
<операторы>
END [ <метка> ];
Каждое объявление и каждый оператор внутри блока завершаются точкой с запятой. Блок, расположенный внутри другого блока, должен иметь точку с запятой после END
, как показано выше. Однако последний END
, завершающий тело функции, не требует точки с запятой.
Метка
необходима только в том случае, если вы хотите идентифицировать блок для использования в операторе EXIT
или уточнить имена переменных, объявленных в блоке. Если после END
указана метка
, она должна совпадать с меткой
в начале блока.
PL/pgSQL позволяет использовать те же комментарии, что и обычный SQL. Двойное тире (--
) начинает однострочный комментарий, блочный комментарий начинается с /*
и заканчивается */
.
Любой оператор в разделе операторов блока может быть подблоком. Подблоки используются для логической группировки или для локализации переменных в небольшой группе операторов. Одноименные переменные, объявленные в подблоке, заменяют любые переменные внешних блоков, но вы можете получить доступ к внешним переменным, если уточните имена с помощью метки их блока. Например:
CREATE FUNCTION my_function() RETURNS integer AS $$
<< my_label >>
DECLARE
variable1 integer := 100;
BEGIN
RAISE NOTICE 'Variable1 is %', variable1; -- Выведет 100
variable1 := 50;
-- Создание подблока
DECLARE
variable1 integer := 200;
BEGIN
RAISE NOTICE 'Variable1 is %', variable1; -- Выведет 200
RAISE NOTICE 'Variable1 is %', my_label.variable1; -- Выведет 50
END;
RAISE NOTICE 'Variable1 is %', variable1; -- Выведет 50
RETURN variable1;
END;
$$ LANGUAGE plpgsql;
SELECT my_function();
Результат:
NOTICE: Variable1 is 100 NOTICE: Variable1 is 200 NOTICE: Variable1 is 50 NOTICE: Variable1 is 50 my_function ------------- 50
Важно не путать использование BEGIN/END
для группировки операторов в PL/pgSQL с одноименными командами SQL для управления транзакциями. В PL/pgSQL BEGIN/END
используются только для группировки. Они не начинают и не завершают транзакцию. Дополнительную информацию о том, как работать с транзакциями в PL/pgSQL, можно найти в статье Transaction management. Кроме того, блок с выражением EXCEPTION
по сути создает вложенную транзакцию, откат которой можно выполнить, не затрагивая внешнюю транзакцию. Это описано в разделе Trapping errors.
Объявления
Все переменные, используемые в блоке, должны быть объявлены в разделе блока DECLARE
. Единственным исключением является переменная цикла FOR
. Переменные PL/pgSQL могут иметь любой тип данных SQL, например integer, varchar, char и т.д.
Примеры объявлений переменных:
account_id integer;
amount numeric(5);
article varchar;
my_row tablename%ROWTYPE;
my_field tablename.columnname%TYPE;
arow RECORD;
Общий синтаксис объявления переменных:
<имя> [ CONSTANT ] <тип> [ COLLATE <имя_правила_сортировки> ] [ NOT NULL ] [ { DEFAULT | := | = } <выражение> ];
имя |
Имя переменной |
CONSTANT |
Предотвращает изменение значения переменной после инициализации, таким образом значение остается постоянным в блоке |
тип |
Тип переменной |
COLLATE <имя_правила_сортировки> |
Определяет параметры сортировки, которые будут использоваться для переменной (см. Collation of PL/pgSQL variables) |
NOT NULL |
Если указано, присвоение значения |
{DEFAULT | := | = } <выражение> |
Указывает начальное значение, присвоенное переменной при входе в блок. Если выражение |
Вместо Oracle PL/SQL-совместимого присваивания :=
можно использовать =
.
Примеры:
account_id CONSTANT integer := 10;
quantity integer DEFAULT 32;
article varchar = 'This is text';
Объявление параметров функции
Параметры, передаваемые функциям, именуются идентификаторами $1
, $2
и т.д. При желании для имен параметров можно объявить псевдонимы для повышения читабельности кода. Получить значение параметра можно используя псевдоним или числовой идентификатор. Следующие две функции идентичны:
CREATE FUNCTION deposit_interest(real) RETURNS real AS $$
BEGIN
RETURN $1 * 1.2;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION deposit_interest(total real) RETURNS real AS $$
BEGIN
RETURN total * 1.2;
END;
$$ LANGUAGE plpgsql;
Функции PL/pgSQL также поддерживают выходные (output) параметры. Выходным параметрам присваиваются числовые идентификаторы $1…$n
и им можно задать псевдонимы так же, как и входным параметрам. Выходной параметр — переменная, которую необходимо определить во время выполнения функции. Возвращается последнее присвоенное значение. Выходные параметры наиболее полезны при возврате нескольких значений:
CREATE FUNCTION calculations (x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
sum := x + y;
prod := x * y;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM calculations(10, 5);
Результат:
sum | prod -----+------ 15 | 50
Псевдонимы
Можно использовать выражение ALIAS FOR
, чтобы объявить псевдоним для переменной или параметра функции.
<новое_имя> ALIAS FOR <старое_имя>;
В следующем примере создается псевдоним variable2
для переменной variable1
:
DECLARE
variable2 ALIAS FOR variable1;
В приведенном ниже примере объявляется псевдоним parameter2
для второго параметра функции:
DECLARE
parameter2 ALIAS FOR $2;
Копирование типов
%TYPE
позволяет присваивать новой переменной тип данных существующей переменной или столбца таблицы. При использовании %TYPE
не нужно знать тип данных объекта, на который вы ссылаетесь. Кроме того, если тип данных элемента, на который ссылаются, изменится в будущем, вам не нужно будет менять определение функции.
Следующий код показывает, как объявлять переменные с типом данных столбца таблицы и другой переменной:
variable1 pgbench_accounts.abalance%TYPE;
variable3 variable2%TYPE;
Типы строки
Переменная составного типа называется переменной строки или переменной типа строки (row-type). Такая переменная может содержать целую строку результата запроса SELECT
или FOR
. Вы можете объявить переменную строки того же типа, что и строки существующей таблицы или представления. Используйте для этого нотацию <имя_таблицы>%ROWTYPE
:
row_var pgbench_accounts%ROWTYPE;
Доступ к отдельным полям значения строки осуществляется с использованием точки, например my_row.field
.
Параметр функции может иметь составной тип (строка таблицы). В этом случае соответствующий идентификатор $n
будет переменной-строкой, а поля из него выбираются следующим образом: $1.field1
. В приведенном ниже примере используются таблицы из статьи Пример работы с таблицами через psql.
CREATE FUNCTION get_author(t_row book) RETURNS text AS $$
DECLARE
author text;
BEGIN
SELECT name INTO author FROM author WHERE id = t_row.author_id;
RETURN author;
END;
$$ LANGUAGE plpgsql;
SELECT get_author(b.*), title FROM book b;
Результат:
get_author | title ---------------------+----------------------- Virginia Woolf | Mrs. Dalloway Virginia Woolf | To the Lighthouse Harper Lee | To Kill a Mockingbird F. Scott Fitzgerald | The Great Gatsby J.R.R. Tolkien | The Lord of the Rings George Orwell | 1984 George Orwell | Animal Farm
Тип record
Переменные record аналогичны переменным типа строки, но у них нет предопределенной структуры. Они принимают структуру строки, назначенной им во время команды SELECT
или FOR
. Структура переменной типа record может быть изменена каждый раз при её назначении. Пока переменная типа record не будет установлена впервые, она не имеет структуры и любая попытка доступа к её полю приведет к ошибке во время выполнения.
Пример:
CREATE FUNCTION get_book_name(int) RETURNS text AS $$
DECLARE
my_row record;
BEGIN
SELECT * INTO my_row FROM book WHERE id = $1;
RETURN my_row.title;
END;
$$ LANGUAGE plpgsql;
SELECT get_book(5);
Результат:
get_book_name ----------------------- The Lord of the Rings
Выполнение SQL-команд
Любую команду SQL, которая не возвращает строки, можно выполнить внутри функции PL/pgSQL, просто написав команду. Например, можно создать и заполнить таблицу:
CREATE TABLE shelves (
book_id SERIAL PRIMARY KEY,
location VARCHAR(10) NOT NULL);
INSERT INTO shelves VALUES (5, 'B32');
Когда команда возвращает строки (например, SELECT
или INSERT/UPDATE/DELETE
с RETURNING
), есть две возможности. Если команда возвращает не более одной строки или вас интересует только первая строка вывода, используйте выражение INTO
для сохранения вывода. См. Executing a command with a single-row result. INTO
используется в примере выше:
...
SELECT * INTO my_row FROM book WHERE id = $1;
...
Чтобы обработать все выходные строки, напишите команду в качестве источника данных для цикла FOR
, как описано в Looping through query results. Следующая функция обновляет все материализованные представления, используя этот подход:
CREATE FUNCTION refresh_mat_views() RETURNS integer AS $$
DECLARE
mviews RECORD;
BEGIN
RAISE NOTICE 'Refreshing all materialized views...';
FOR mviews IN
SELECT n.nspname AS mv_schema,
c.relname AS mv_name,
pg_catalog.pg_get_userbyid(c.relowner) AS owner
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
WHERE c.relkind = 'm'
ORDER BY 1
LOOP
-- В "mviews" есть одна запись для каждого материализованного представления
RAISE NOTICE 'Refreshing materialized view %.% (owner: %)...',
quote_ident(mviews.mv_schema),
quote_ident(mviews.mv_name),
quote_ident(mviews.owner);
EXECUTE format('REFRESH MATERIALIZED VIEW %I.%I', mviews.mv_schema, mviews.mv_name);
END LOOP;
RAISE NOTICE 'Done refreshing materialized views.';
RETURN 1;
END;
$$ LANGUAGE plpgsql;
SELECT refresh_mat_views();
Неоптимизируемые команды SQL (также называемые служебными командами) не принимают параметры запроса. В таких командах не работает автоматическая подстановка переменных PL/pgSQL. Чтобы включить непостоянный текст в служебную команду, выполняемую из PL/pgSQL, создайте команду как строку, а затем используйте EXECUTE
для её запуска, как описано в Executing dynamic commands. В приведенном выше примере EXECUTE
используется для запуска REFRESH MATERIALIZED VIEW
:
...
EXECUTE format('REFRESH MATERIALIZED VIEW %I.%I', mviews.mv_schema, mviews.mv_name);
...
Иногда необходимо вычислить выражение или запрос SELECT
, но отбросить результат, например, при вызове функции, у которой есть побочные эффекты, но нет полезного результата. Чтобы сделать это в PL/pgSQL, используйте оператор PERFORM
:
PERFORM <query>;
Пример:
PERFORM pg_advisory_lock(1234);
Управляющие структуры
Управляющие структуры, вероятно, являются наиболее полезной частью PL/pgSQL. С помощью управляющих структур вы можете очень гибко и мощно организовывать данные в PostgreSQL.
RETURN
RETURN
позволяет возвращать данные из функции.
RETURN
, для которого указано выражение, завершает функцию и возвращает значение выражения вызывающей стороне. Эта форма используется для функций PL/pgSQL, которые не возвращают набор. В примерах выше RETURN
вызывается следующим образом:
...
RETURN total * 1.2;
...
RETURN my_row.title;
...
Когда объявлено, что функция PL/pgSQL возвращает SETOF <sometype>
, отдельные возвращаемые элементы определяются последовательностью команд RETURN NEXT
или RETURN QUERY
, а затем последняя команда RETURN
без аргументов используется для обозначения того, что функция завершила выполнение. RETURN NEXT
можно использовать как со скалярными, так и с составными типами данных; при составном типе результата будет возвращена целая "таблица" результатов. RETURN QUERY
добавляет результаты выполнения запроса к набору результатов функции.
CREATE OR REPLACE FUNCTION get_all_books() RETURNS SETOF book AS $$
DECLARE
row book%ROWTYPE;
BEGIN
FOR row IN SELECT * FROM book
LOOP
-- здесь можно произвести необходимую обработку значений
-- таблица "book" не изменяется, строка "processed by RETURN NEXT" добавляется только в вывод
row.title= row.title||' processed by RETURN NEXT';
RETURN NEXT row;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
SELECT title FROM get_all_books();
Результат:
title ------------------------------------------------ The Great Gatsby processed by RETURN NEXT The Lord of the Rings processed by RETURN NEXT 1984 processed by RETURN NEXT Animal Farm processed by RETURN NEXT Mrs. Dalloway processed by RETURN NEXT To the Lighthouse processed by RETURN NEXT To Kill a Mockingbird processed by RETURN NEXT
CREATE OR REPLACE FUNCTION get_books_by_author(int) RETURNS SETOF book AS $$
BEGIN
RETURN QUERY SELECT * FROM book WHERE author_id=$1;
IF NOT FOUND THEN
RAISE EXCEPTION 'No books';
END IF;
RETURN;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_books_by_author(3);
Результат:
id | title | author_id | public_year ----+------------------+-----------+------------- 4 | The Great Gatsby | 3 | 1925
Процедуры не имеют возвращаемого значения. Таким образом, процедура может завершиться без оператора RETURN
. Можно использовать оператор RETURN
для досрочного выхода из кода, для этого напишите RETURN
без выражения. Если процедура имеет выходные параметры, вызывающей стороне будут возвращены последние значения переменных выходных параметров.
Вызов процедур
Функция, процедура или блок DO
в PL/pgSQL могут вызывать процедуры с помощью CALL
. Выходные параметры обрабатываются иначе, чем CALL
в простом SQL. Каждый параметр процедуры OUT
или INOUT
должен соответствовать переменной в инструкции CALL
, и все, что возвращает процедура, присваивается обратно этой переменной после её возврата. Например:
CREATE PROCEDURE triple(INOUT x int)
LANGUAGE plpgsql
AS $$
BEGIN
x := x * 3;
END;
$$;
DO $$
DECLARE myvar int := 5;
BEGIN
CALL triple(myvar);
RAISE NOTICE 'myvar = %', myvar; -- выведет 15
END;
$$;
Условные операторы
Операторы IF
и CASE
позволяют выполнять альтернативные варианты команд в зависимости от определенных условий. В PL/pgSQL доступны три формы IF
:
-
IF … THEN … END IF
-
IF … THEN … ELSE … END IF
-
IF … THEN … ELSIF … THEN … ELSE … END IF
и две формы CASE
:
-
CASE … WHEN … THEN … ELSE … END CASE
-
CASE WHEN … THEN … ELSE … END CASE
Пример использования IF
:
IF number = 0 THEN
result := 'zero';
ELSIF number > 0 THEN
result := 'positive';
ELSIF number < 0 THEN
result := 'negative';
ELSE
-- единственная оставшаяся возможность -- значение равно null
result := 'NULL';
END IF;
Простая форма CASE
обеспечивает условное выполнение, основанное на равенстве операндов. Логическое выражение оценивается один раз и последовательно сравнивается с каждым выражением в предложениях WHEN
. Если совпадение найдено, соответствующие операторы выполняются, а затем управление передается следующему оператору после END CASE
.
CASE <логическое_выражение>
WHEN <выражение> [, <выражение> [ ... ]] THEN
<операторы>
[ WHEN <выражение> [, <выражение> [ ... ]] THEN
<операторы>
... ]
[ ELSE
<операторы> ]
END CASE;
Пример:
CASE book_id
WHEN 1,2 THEN
msg := 'Author is Virginia Woolf';
WHEN 3 THEN
msg := 'Author is Harper Lee';
WHEN 4 THEN
msg := 'Author is F. Scott Fitzgerald';
WHEN 5 THEN
msg := 'Author is J.R.R. Tolkien';
WHEN 6,7 THEN
msg := 'Author is George Orwell';
ELSE
msg := 'Author is unknown';
END CASE;
CASE
с перебором условий обеспечивает условное выполнение, основанное на истинности логических выражений. Логическое выражение каждого WHEN
оценивается по очереди, пока не будет найдено истинное выражение. Затем выполняются соответствующие операторы, и управление передается следующему оператору после END CASE
. Последующие выражения в оставшихся WHEN
не оцениваются. Если истинный результат не найден, выполняются операторы ELSE
. Если ELSE
не указано, возникает исключение CASE_NOT_FOUND
.
CASE
WHEN <логическое_выражение> THEN
<операторы>
[ WHEN <логическое_выражение> THEN
<операторы>
... ]
[ ELSE
<операторы>]
END CASE;
Пример:
CASE
WHEN balance > 1000 THEN
status = 'Platinum' ;
WHEN balance > 500 THEN
status = 'Gold' ;
ELSE
status = 'Base';
END CASE;
Циклы
С помощью операторов LOOP
, EXIT
, CONTINUE
, WHILE
, FOR
и FOREACH
можно организовать повторение серии команд в коде PL/pgSQL.
LOOP
определяет безусловный цикл, который повторяется бесконечно, пока не будет завершен оператором EXIT
или RETURN
. Необязательная метка
может использоваться операторами EXIT
и CONTINUE
во вложенных циклах, чтобы указать, к какому циклу относятся эти операторы.
[ <<<метка>>> ]
LOOP
<операторы>
END LOOP [ <метка> ];
EXIT [ <метка> ] [ WHEN <логическое_выражение> ];
Оператор EXIT
осуществляет выход из цикла. Если для EXIT
не указана метка
, самый внутренний цикл завершается и следующим выполняется оператор, следующий за END LOOP
. Если указана метка
, она должна быть меткой текущего или внешнего уровня вложенного цикла или блока. Затем именованный цикл или блок завершается, и управление продолжается с помощью оператора после соответствующего END
.
Если указано WHEN
, выход из цикла происходит только в том случае, если логическое выражение истинно. В противном случае управление передается оператору после EXIT
.
EXIT
можно использовать со всеми типами циклов, он не ограничивается использованием в безусловных циклах.
CONTINUE [ <метка> ] [ WHEN <логическое_выражение> ];
Оператор CONTINUE
прерывает текущую и начинает новую итерацию цикла. Если метка не указана, начинается следующая итерация самого внутреннего цикла. Все операторы, оставшиеся в теле цикла, пропускаются, и управление возвращается к выражению управления циклом (если оно есть), чтобы определить, нужна ли еще одна итерация цикла. Если метка добавлена, она определяет цикл, выполнение которого будет продолжено.
Если указано WHEN
, следующая итерация цикла начинается только в том случае, если логическое выражение истинно. В противном случае управление передается оператору после CONTINUE
.
CONTINUE
можно использовать со всеми типами циклов, он не ограничивается использованием в безусловных циклах.
В следующем примере используются LOOP
, EXIT
и CONTINUE
для вычисления суммы четных чисел от 1
до заданного параметра:
CREATE OR REPLACE FUNCTION get_sum(int) RETURNS int AS $$
DECLARE
num int := 0;
my_sum int :=0;
BEGIN
LOOP
num = num + 1;
-- Выйти из цикла, если num > переданного параметра
EXIT WHEN num > $1;
-- Пропустить итерацию, если num является нечетным числом
CONTINUE WHEN mod(num,2) != 0;
my_sum = my_sum + num;
END LOOP;
RETURN my_sum;
END;
$$ LANGUAGE plpgsql;
SELECT get_sum(12);
Результат:
get_sum --------- 42
Оператор цикла WHILE
имеет следующий синтаксис:
[ <<<метка>>> ]
WHILE <логическое_выражение> LOOP
<операторы>
END LOOP [ <метка> ];
Оператор WHILE
повторяет последовательность операторов до тех пор, пока логическое выражение истинно. Выражение проверяется непосредственно перед каждым входом в тело цикла.
В этом коде с помощью WHILE
переписана функция get_sum
из примера выше, которая вычисляет сумму четных чисел:
CREATE OR REPLACE FUNCTION get_sum(int) RETURNS int AS $$
DECLARE
my_sum int :=0;
num int :=0;
BEGIN
WHILE num <= $1 LOOP
num = num +1;
IF mod(num,2) = 0 THEN
my_sum = my_sum + num;
END IF;
END LOOP;
RETURN my_sum;
END;
$$ LANGUAGE plpgsql;
SELECT get_sum(12);
Результат:
get_sum --------- 42
Синтаксис оператора цикла FOR
, который перебирает диапазон целых чисел:
[ <<метка>> ]
FOR <счетчик> IN [ REVERSE ] <выражение> .. <выражение> [ BY <выражение> ] LOOP
<операторы>
END LOOP [ метка ];
Переменная counter
определена как целое число и существует только внутри цикла (любое существовавшее ранее определение переменной counter
внутри цикла игнорируется). Два выражения, задающие нижнюю и верхнюю границу диапазона, вычисляются один раз при входе в цикл. Если предложение BY
не указано, шагом итерации является 1
, в противном случае это значение, указанное в предложении BY
. Если указано REVERSE
, значение шага вычитается, а не добавляется после каждой итерации.
В этом коде с помощью FOR
переписана функция get_sum
из примера выше, которая вычисляет сумму четных чисел:
CREATE OR REPLACE FUNCTION get_sum(int) RETURNS int AS $$
DECLARE
my_sum int :=0;
BEGIN
FOR i in 1..$1 LOOP
IF mod(i,2) = 0 THEN
my_sum = my_sum + i;
END IF;
END LOOP;
RETURN my_sum;
END;
$$ LANGUAGE plpgsql;
SELECT get_sum(12);
Результат:
get_sum --------- 42
Кроме того, в PL/pgSQL есть цикл FOREACH
для перебора элементов массива. См. Looping through arrays.
Пример
В этом примере создаются таблицы со случайными данными.
-
Создайте функцию, которая возвращает строку указанной длины из заданных символов:
CREATE OR REPLACE FUNCTION generate_text(len int) RETURNS text AS $$ DECLARE symbols text := 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'; result text := ''; place int := 0; symbols_lenght int = length(symbols); BEGIN WHILE LENGTH(result) < len LOOP place = floor(random()*symbols_lenght); result := result || SUBSTRING(symbols, place, 1); END LOOP; RETURN result; END $$ LANGUAGE 'plpgsql';
-
Напишите процедуру, которая создает указанное количество таблиц, используя функцию
generate_text
для их заполнения. Необходимое количество таблиц передается в качестве параметра:CREATE OR REPLACE PROCEDURE create_tables(num int, t_name text, row_number int)AS $$ DECLARE table_name text; BEGIN FOR i IN 1 .. num LOOP table_name= t_name || '_' || i; EXECUTE ('CREATE TABLE ' || table_name || '(id int, text varchar);'); FOR j IN 1 .. row_number LOOP -- Номер строки определяет количество символов в столбце "текст" EXECUTE ('INSERT INTO ' || table_name || ' VALUES (' || j ||','||''''|| generate_text(j)||'''' || ');'); END LOOP; END LOOP; END $$ LANGUAGE 'plpgsql';
-
Вызовите процедуру
create_tables
. Первый параметр (3
) указывает количество создаваемых таблиц, второй параметр (my_table
) задает префикс для имен таблиц, а третий (25
) — количество строк в таблицах.CALL create_tables(3, 'my_table', 25);
В результате будут созданы таблицы
my_table_1
,my_table_2
иmy_table_3
.