Введение
Microsoft SQL Server — широко используемая в корпоративной среде (особенно в организациях, уже глубоко интегрированных с экосистемой Microsoft) СУБД со своим процедурным диалектом T-SQL — в статье разберём ключевые особенности T-SQL, отличающие его от уже разобранных диалектов PostgreSQL/MySQL.
Концепция
T-SQL (Transact-SQL) расширяет стандартный SQL собственным синтаксисом для переменных, управления потоком выполнения, обработки ошибок (TRY/CATCH, концептуально похожим на обработку исключений в обычных языках программирования) и специфичными функциями (TOP вместо LIMIT, отличающийся синтаксис работы с датами). Хранимые процедуры (статья 411) особенно широко используются в традиционной разработке на SQL Server, часто значительно активнее, чем в типичной практике PostgreSQL/MySQL проектов, что отражает историческую архитектурную традицию, распространённую в корпоративных .NET-приложениях, тесно интегрированных с SQL Server.
Пример кода: Microsoft SQL Server
-- TOP вместо LIMIT — выборка ограниченного количества строк (синтаксис T-SQL)
SELECT TOP 10 name, total_amount FROM orders ORDER BY total_amount DESC;
-- ^ в PostgreSQL/MySQL это было бы: SELECT ... ORDER BY ... LIMIT 10 (статья 427)
-- TRY/CATCH — обработка ошибок в T-SQL, аналогично try/catch обычных языков программирования
BEGIN TRY
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
PRINT 'Ошибка при переводе средств: ' + ERROR_MESSAGE();
END CATCH;
-- Переменные и управление потоком — T-SQL близок к процедурному стилю программирования
DECLARE @customer_id INT = 5;
DECLARE @total_spent DECIMAL(12, 2);
SELECT @total_spent = SUM(total_amount) FROM orders WHERE customer_id = @customer_id;
IF @total_spent > 10000
PRINT 'VIP-клиент';
ELSE
PRINT 'Обычный клиент';
-- Хранимая процедура T-SQL с входными и выходными параметрами
CREATE PROCEDURE GetCustomerDiscount
@customer_id INT,
@discount_rate DECIMAL(5, 2) OUTPUT
AS
BEGIN
DECLARE @total_spent DECIMAL(12, 2);
SELECT @total_spent = COALESCE(SUM(total_amount), 0) FROM orders WHERE customer_id = @customer_id;
SET @discount_rate = CASE
WHEN @total_spent > 50000 THEN 0.15
WHEN @total_spent > 10000 THEN 0.10
ELSE 0.0
END;
END;
Пояснения к коду
SELECT TOP 10 ... ORDER BY ... показывает один из самых заметных синтаксических отличий T-SQL от PostgreSQL/MySQL — ограничение количества строк выражается через TOP сразу после SELECT, а не через LIMIT в конце запроса, что для разработчиков, переходящих между СУБД, требует явного пересмотра привычного синтаксиса (подробнее систематизировано в статье 427). TRY/CATCH с ERROR_MESSAGE() показывает структурированную обработку ошибок, более близкую по стилю к обычным языкам программирования по сравнению с менее формализованным подходом некоторых других СУБД, что отражает общую тенденцию T-SQL быть более процедурно ориентированным языком, активно используемым для построения значительной части бизнес-логики непосредственно внутри БД (статья 411), а не просто декларативным языком запросов.
Подводные камни
- Прямой перенос T-SQL кода (хранимых процедур, синтаксиса запросов) на PostgreSQL/MySQL без понимания, что синтаксис существенно отличается —
TOP,TRY/CATCH, специфичный синтаксис переменных и многие функции T-SQL не работают «как есть» в других СУБД, и перенос проекта с SQL Server на другую СУБД (или наоборот) почти всегда требует существенной переработки любого нетривиального, специфичного для T-SQL кода, а не простого, механического копирования. - Чрезмерный перенос бизнес-логики в хранимые процедуры T-SQL по исторически сложившейся традиции, не подвергая сомнению, действительно ли это лучшее архитектурное решение для конкретного нового проекта — хотя эта традиция широко распространена в экосистеме SQL Server/.NET, общие компромиссы хранимых процедур (статья 411 — снижение переносимости, усложнение тестирования) остаются актуальными и для T-SQL, и решение о размещении логики в хранимых процедурах должно быть осознанным архитектурным выбором, а не автоматическим следствием исторической традиции конкретной технологической экосистемы.
- Игнорирование различий в обработке
NULLи иных тонких семантических деталей между T-SQL и стандартным SQL/другими диалектами — хотя основные принципы работы с NULL (статья 396) общие для всех диалектов SQL, отдельные детали поведения (например, конкатенация строк с NULL некоторыми настройками может вести себя иначе, чем в PostgreSQL) могут отличаться, и код, перенесённый между диалектами без тщательной проверки, может содержать незаметные, тонкие расхождения в поведении именно в граничных случаях с NULL. - Использование устаревших возможностей T-SQL без оглядки на актуальные рекомендации Microsoft для текущей версии SQL Server — как и любая активно развивающаяся технология, T-SQL со временем добавляет новые, более удобные конструкции (взаимодействие с JSON, улучшенная поддержка оконных функций) и постепенно выводит из рекомендуемого использования некоторые более старые подходы, и для нового кода стоит ориентироваться на актуальную документацию конкретной используемой версии, а не на потенциально устаревшие примеры из давних источников.