Исторически задачи динамических отчётов с переменным числом группировок не предусматривались стандартом SQL — он рассчитан на статически описанные запросы с чётко определённой схемой результата. С появлением BI-платформ и интерактивных дашбордов программисты стали искать способы конструировать SQL-запросы "на лету", чтобы по запросу пользователя формировать сложные множественные срезы данных.
Проблема — SQL нельзя программировать обычной логикой if/else внутри SELECT — число столбцов, сами поля и даже GROUP BY определяется только на этапе компиляции запроса. Если пользователь желает срез по нескольким произвольным полям, приходится динамически собирать текст запроса и исполнять его через EXECUTE/Dynamic SQL.
Решение:
Реализуют генерацию SQL-кода во внешнем приложении с последующим вызовом через EXEC/EXECUTE. В некоторых случаях для простых задач используют CASE и жёсткие шаблоны, но для гибкости всегда прибегают к динамическому SQL:
Пример кода (Pseudocode):
-- На стороне приложения (например, Python) groups = ['region', 'channel', 'month'] columns = [f'SUM({col}) AS {col}_sum' for col in selected_metrics] group_by = ', '.join(groups) selects = ', '.join(groups + columns) query = f'SELECT {selects} FROM sales GROUP BY {group_by}' -- Затем отправка этого запроса через application/sql-интерфейс
В СУБД с поддержкой EXECUTE:
DECLARE @sql NVARCHAR(MAX); SET @sql = N'SELECT ' + @selects + ' FROM sales GROUP BY ' + @group_by + ';'; EXEC sp_executesql @sql;
Ключевые особенности:
Можно ли реализовать динамическое изменение числа столбцов (pivot/unpivot) только через стандартный SELECT без динамического SQL?
Нет — только заранее известные значения можно "развернуть" через CASE/DECODE, но неизвестное число колонок достигается только динамически.
Гарантирует ли ваша логика динамического SQL защиту от инъекций, если используете string concatenation?
Нет, ручная конкатенация строк — постоянный риск SQL Injection. Обязательно валидировать список полей/группировок по whitelisting, избегать пользовательских фрагментов без проверки, а ещё лучше использовать параметры, где возможно.
Может ли GROUP BY принимать список столбцов через переменную?
Стандартный SQL не поддерживает передачу списка полей в GROUP BY через переменную/параметр. Необходимо динамически формировать текст запроса — просто использовать переменную в списке GROUP BY нельзя.
BI-инженер позволил пользователю прямо из интерфейса приложения передавать имена полей для отчёта — не фильтруя их по whitelisting. В результате при тесте инъекций была "уронена" прод-таблица из-за внедрённого вредоносного кода в имени поля.
Плюсы:
Минусы:
Инженер внедрил строгую валидацию имен — пользователь мог выбирать только из разрешённых колонок (из config/metadata), динамический SQL собирался только по whitelisting, не было путей для инъекции.
Плюсы:
Минусы: