С каким функционалом Transact-SQL (T-SQL) вы работали на предыдущем месте?

Ответ

На проектах с SQL Server активно использовал следующий функционал T-SQL:

  • Хранимые процедуры и функции: Для инкапсуляции бизнес-логики, повышения безопасности и производительности.
  • Триггеры (DML): В основном для аудита изменений и поддержания сложной целостности данных, с осторожностью из-за влияния на производительность.
  • CTE (Common Table Expressions) и рекурсивные запросы: Для работы с иерархическими данными и построения сложных, читаемых запросов.
  • Оконные функции: ROW_NUMBER(), RANK(), SUM() OVER() для аналитики без группировки исходных строк.
  • Динамический SQL: Для построения гибких запросов, где структура запроса зависит от параметров. Всегда с параметризацией для защиты от SQL-инъекций.
    DECLARE @TableName sysname = 'Users', @Id INT = 5;
    DECLARE @Sql NVARCHAR(MAX);
    SET @Sql = N'SELECT * FROM ' + QUOTENAME(@TableName) + ' WHERE Id = @ParamId';
    EXEC sp_executesql @Sql, N'@ParamId INT', @ParamId = @Id;
  • Явное управление транзакциями: BEGIN TRAN, COMMIT, ROLLBACK для атомарности операций.
  • Временные таблицы (#temp) и табличные переменные (@table): Для хранения промежуточных результатов.
  • Оператор MERGE: Для выполнения операций "upsert" (обновить или вставить) одной командой.
  • PIVOT/UNPIVOT: Для преобразования данных из строк в столбцы и обратно.

Ответ 18+ 🔞

А, ну ты спрашиваешь, на чём я собаку съел в SQL Server? Давай по полочкам разложу, как я с этой штукой работал, чтобы не просто работало, а ещё и не ебало мозги потом всем, кто это читать будет.

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

Триггеры... О, это отдельная песня. Использовал, но с оглядкой, как на мину. В основном для аудита — кто, когда и что поменял в таблице. Или для поддержания какой-нибудь ебучей целостности, которую на уровне ограничений не опишешь. Но блядь, если навесить их дохуя на активную таблицу, они так могут производительность просадить, что мало не покажется. Поэтому осторожно, как с огнём.

CTE и рекурсивные запросы — вещь просто охуенная для иерархий. Всякие деревья категорий, оргструктуры — красота. И обычные CTE просто для читаемости больших запросов: разбил сложную хуйню на логические куски, и всё сразу понятнее становится.

Оконные функции — это просто магия. ROW_NUMBER() для пагинации или выборки уникальных строк, RANK(), SUM() OVER() для всяких скользящих итогов и аналитики прямо в выборке, без дурацких группировок, которые всю выборку ломают. Прям чувствуешь себя крутым, когда применяешь.

Динамический SQL... Вот тут надо с умом, а то такую дыру в безопасности проделаешь, что все данные утекут. Я его использовал, когда нужно было гибко собирать запрос: например, фильтры по разным полям, которые могут быть, а могут и не быть. Но главное правило — всё параметризовать, как в примере. Никаких прямых подстановок строк от пользователя, иначе SQL-инъекция обеспечена. Смотри, как правильно:

DECLARE @TableName sysname = 'Users', @Id INT = 5;
DECLARE @Sql NVARCHAR(MAX);
SET @Sql = N'SELECT * FROM ' + QUOTENAME(@TableName) + ' WHERE Id = @ParamId';
EXEC sp_executesql @Sql, N'@ParamId INT', @ParamId = @Id;

Видишь? Имя таблицы экранирую через QUOTENAME, а значение параметра передаю отдельно. Так никакая инъекция не пролезет.

Явные транзакции — это когда нужно, чтобы несколько операций прошли как одна, атомарно. Либо всё хорошо, либо откатываемся к чёртовой матери, если где-то ошибка. BEGIN TRAN, потом если всё ок — COMMIT, если пиздец — ROLLBACK. Главное, не забывать их закрывать, а то повесишь блокировку на всю систему.

Временные таблицы (#temp) и табличные переменные (@table) — для промежуточных результатов. #temp — пообъёмнее, поиндексовать можно, живёт до конца сессии. @table — помельче, для быстрых штук, обычно в памяти. Выбирал по ситуации.

MERGE — оператор-комбайн. Нужно обновить запись, если есть, а если нет — вставить? Вместо кучи IF EXISTS просто пишешь один красивый MERGE, и он сам всё делает. Удобно, ёпта.

Ну и PIVOT/UNPIVOT. Когда нужно было отчёты строить и превращать строки в столбцы (или наоборот). Выглядит немного магически, но когда разберёшься — мощный инструмент. Хотя иногда проще на стороне приложения это сделать, если логика слишком замудрённая.

Вот так, вкратце. Всё это использовал не абы как, а с пониманием, где и зачем. Чтобы работало быстро, безопасно и не приходилось потом ночами разгребать последствия кривых решений.