JavaRush /Курсы /SQL SELF /Вложенные вызовы процедур с EXECUTE: динамическое выполне...

Вложенные вызовы процедур с EXECUTE: динамическое выполнение SQL-кода

SQL SELF
53 уровень , 4 лекция
Открыта

Прежде чем мы перейдем к практике, давайте ответим на вопрос: что же такое динамический SQL? Представьте, что вам нужно создать таблицу с уникальным именем, которое передается в качестве параметра. Или выполнить запрос к таблице, имя которой определяется на стадии выполнения программы. Здесь простого статического SQL недостаточно — вот где приходит на помощь динамическое выполнение.

PL/pgSQL предоставляет команду EXECUTE, которая выполняет SQL-запрос, переданный в виде строки. Это дает вам возможность строить и запускать SQL-код "на лету", создавая запросы, которые отличаются в зависимости от параметров.

Причины, по которым динамический SQL может быть полезным:

  1. Гибкость: Возможность строить запросы динамически в зависимости от входных данных. Например, выполнение операций над таблицами или колонками, имена которых неизвестны заранее.
  2. Автоматизация: Создание таблиц или индексов с уникальными именами.
  3. Универсальность: Возможность работы с разной структурой данных без необходимости переписывать процедуру.

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

Синтаксис EXECUTE

Использование динамического SQL через EXECUTE выглядит следующим образом:

EXECUTE 'SQL-строка';

Пример простого запроса:

DO $$
BEGIN
  EXECUTE 'CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT)';
END $$;

Этот блок кода создаст таблицу test_table. Всё просто, но давайте разберемся с более сложными сценариями.

Примеры использования EXECUTE

1. Создание таблицы с динамическим именем

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

DO $$
DECLARE
  table_name TEXT;
BEGIN
  -- Генерируем имя таблицы
  table_name := 'report_' || to_char(CURRENT_DATE, 'YYYYMMDD');

  -- Создаем таблицу с динамическим именем
  EXECUTE 'CREATE TABLE ' || table_name || ' (id SERIAL PRIMARY KEY, data TEXT)';

  -- Выводим сообщение для проверки
  RAISE NOTICE 'Таблица % успешно создана', table_name;
END $$;

Здесь динамическое имя генерируется из текущей даты, а итоговая SQL-строка передается в EXECUTE.

2. Выполнение запроса с динамическими параметрами

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

CREATE OR REPLACE FUNCTION get_data_from_table(table_name TEXT)
RETURNS TABLE(id INTEGER, name TEXT) AS $$
BEGIN
  RETURN QUERY EXECUTE
    'SELECT id, name FROM ' || table_name || ' WHERE id < 10';
END $$ LANGUAGE plpgsql;

Вызов функции:

SELECT * FROM get_data_from_table('employees');

Этот подход отлично подходит для построения универсальных утилит, таких как динамические отчетные системы.

Проблемы и ограничения динамического SQL

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

  1. SQL-инъекции: если вы передаете строковые параметры в запрос без обработки, вы можете дать злоумышленнику возможность выполнить произвольный SQL-код.

    Пример уязвимого кода:

    EXECUTE 'SELECT * FROM users WHERE name = ''' || user_input || '''';
    

    Если user_input содержит строку '; DROP TABLE users; --, то запрос уничтожит таблицу users.

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

  3. Потеря производительности: динамические запросы перекрывают механизмы кэширования плана выполнения в PostgreSQL, что может привести к снижению производительности.

Как защититься от SQL-инъекций

Чтобы избежать атак SQL-инъекций, используйте параметризацию в динамических запросах вместо простой конкатенации строк. В PL/pgSQL это достигается с помощью функции quote_literal() для строковых параметров и quote_ident() для идентификаторов (таких как названия таблиц или столбцов).

Пример безопасного кода:

DO $$
DECLARE
  table_name TEXT;
  user_input TEXT := 'John';
BEGIN
  table_name := 'employees';

  EXECUTE 'SELECT * FROM ' || quote_ident(table_name) ||
          ' WHERE name = ' || quote_literal(user_input);
END $$;

Реализация: динамическое обновление таблиц

Вот пример процедуры, которая обновляет значения в таблице с именем, переданным в качестве параметра:

CREATE OR REPLACE FUNCTION update_table_data(table_name TEXT, id_value INT, new_data TEXT)
RETURNS VOID AS $$
BEGIN
  EXECUTE 'UPDATE ' || quote_ident(table_name) ||
          ' SET data = ' || quote_literal(new_data) ||
          ' WHERE id = ' || id_value;
END $$ LANGUAGE plpgsql;

Вызов функции:

SELECT update_table_data('test_table', 1, 'Updated Value');

Пример: создание отчета для клиента

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

CREATE OR REPLACE FUNCTION create_client_report(client_id INT)
RETURNS VOID AS $$
DECLARE
  table_name TEXT;
BEGIN
  -- Формируем имя таблицы отчета
  table_name := 'client_report_' || client_id;

  -- Создаем таблицу для отчета
  EXECUTE 'CREATE TABLE ' || quote_ident(table_name) || ' (order_id INT, amount NUMERIC)';

  -- Заполняем таблицу данными
  EXECUTE 'INSERT INTO ' || quote_ident(table_name) ||
          ' SELECT order_id, amount FROM orders WHERE client_id = ' || client_id;

  RAISE NOTICE 'Отчет для клиента % создан: таблица %', client_id, table_name;
END $$ LANGUAGE plpgsql;

Динамический SQL с EXECUTE — это мощный инструмент, который открывает невероятные возможности для автоматизации и гибкости в PL/pgSQL. Используйте его с осторожностью, помня о рисках SQL-инъекций. Если вы хотите, чтобы ваши запросы были надежными и безопасными, применяйте функции quote_ident() и quote_literal().

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

2
Задача
SQL SELF, 53 уровень, 4 лекция
Недоступна
Создание таблицы с динамическим именем
Создание таблицы с динамическим именем
1
Опрос
Вложенные транзакции, 53 уровень, 4 лекция
Недоступен
Вложенные транзакции
Вложенные транзакции
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Глеб Уровень 59
24 февраля 2026
То ли я ослеп, то ли в курсе нигде не было лекции про функцию format() и про экранирование кавычек...