mcpField
T
tsql

tsql

github.com/claude-dev-suiteデータ・データベース4658 回インストール2 日前に更新
リポジトリ

tsql について

SQL Server で T-SQL を記述する開発者向けのリファレンススキルです。ストアドプロシージャ、関数、制御フロー、エラー処理、トリガー、トランザクション、CTE と、保守性や安全性に配慮した実践方法を扱います。

主な機能

  1. 01ストアドプロシージャと関数の作成
  2. 02制御フローと CTE の利用
  3. 03TRY...CATCH によるエラー処理
  4. 04トリガーとトランザクションの管理
  5. 05動的 SQL とクエリの実践方法

活用シーン

  1. 01SQL Server の業務ロジックを実装
  2. 02プロシージャ内のエラーとトランザクションを処理
  3. 03T-SQL の一般的な問題を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill tsql

インストール後、Claude Code で/tsqlを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

advanced.md
quick-ref/
error-handling.md
functions.md
procedures.md

詳細

T-SQL の基本知識

完全なリファレンス:複数ステートメントの TVF、カスタムエラーメッセージ、INSTEAD OF トリガー、トランザクション分離レベル、カーソル、動的 SQL、再帰 CTE については、advanced.md を参照してください。

詳細な知識:包括的なドキュメントについては、technology に sqlserver を指定して mcp__documentation__fetch_docs を使用してください。

基本構造

-- Anonymous block
BEGIN
    DECLARE @count INT = 0;
    SET @count = @count + 1;
    PRINT 'Count: ' + CAST(@count AS VARCHAR);
END;
GO

変数

DECLARE @name VARCHAR(100) = 'John';
DECLARE @age INT;
DECLARE @salary DECIMAL(10,2), @bonus DECIMAL(10,2);

SET @age = 25;
SELECT @salary = salary FROM employees WHERE id = 1;

-- Multiple assignments
SELECT @salary = salary, @bonus = bonus
FROM employees WHERE id = 1;

-- Table variable
DECLARE @employees TABLE (
    id INT,
    name VARCHAR(100),
    salary DECIMAL(10,2)
);

INSERT INTO @employees SELECT id, name, salary FROM employees;

ストアドプロシージャ

基本的なプロシージャ
CREATE OR ALTER PROCEDURE usp_GetEmployee
    @EmployeeId INT
AS
BEGIN
    SET NOCOUNT ON;

    SELECT employee_id, first_name, last_name, salary
    FROM employees
    WHERE employee_id = @EmployeeId;
END;
GO

-- Execute
EXEC usp_GetEmployee @EmployeeId = 100;
-- or
EXEC usp_GetEmployee 100;
OUTPUT パラメーターを使用するプロシージャ
CREATE OR ALTER PROCEDURE usp_GetEmployeeStats
    @DeptId INT,
    @EmployeeCount INT OUTPUT,
    @TotalSalary DECIMAL(15,2) OUTPUT,
    @AvgSalary DECIMAL(15,2) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    SELECT
        @EmployeeCount = COUNT(*),
        @TotalSalary = SUM(salary),
        @AvgSalary = AVG(salary)
    FROM employees
    WHERE department_id = @DeptId;
END;
GO

-- Call with OUTPUT
DECLARE @Count INT, @Total DECIMAL(15,2), @Avg DECIMAL(15,2);
EXEC usp_GetEmployeeStats
    @DeptId = 10,
    @EmployeeCount = @Count OUTPUT,
    @TotalSalary = @Total OUTPUT,
    @AvgSalary = @Avg OUTPUT;

PRINT 'Count: ' + CAST(@Count AS VARCHAR);
戻り値を返すプロシージャ
CREATE OR ALTER PROCEDURE usp_ValidateEmployee
    @EmployeeId INT
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS (SELECT 1 FROM employees WHERE employee_id = @EmployeeId)
        RETURN -1;  -- Not found

    IF EXISTS (SELECT 1 FROM employees WHERE employee_id = @EmployeeId AND status = 'INACTIVE')
        RETURN -2;  -- Inactive

    RETURN 0;  -- Success
END;
GO

-- Check return value
DECLARE @result INT;
EXEC @result = usp_ValidateEmployee @EmployeeId = 100;

IF @result = 0
    PRINT 'Valid';
ELSE IF @result = -1
    PRINT 'Employee not found';

関数

スカラー関数
CREATE OR ALTER FUNCTION dbo.fn_CalculateBonus(
    @Salary DECIMAL(10,2),
    @YearsOfService INT
)
RETURNS DECIMAL(10,2)
AS
BEGIN
    DECLARE @Bonus DECIMAL(10,2);

    SET @Bonus = CASE
        WHEN @YearsOfService >= 10 THEN @Salary * 0.15
        WHEN @YearsOfService >= 5 THEN @Salary * 0.10
        ELSE @Salary * 0.05
    END;

    RETURN @Bonus;
END;
GO

-- Usage
SELECT employee_id, salary,
    dbo.fn_CalculateBonus(salary, years_of_service) AS bonus
FROM employees;
インラインテーブル値関数
CREATE OR ALTER FUNCTION dbo.fn_GetDeptEmployees(
    @DeptId INT
)
RETURNS TABLE
AS
RETURN (
    SELECT employee_id, first_name, last_name, salary
    FROM employees
    WHERE department_id = @DeptId
);
GO

-- Usage
SELECT * FROM dbo.fn_GetDeptEmployees(10);

制御フロー

IF...ELSE
DECLARE @status VARCHAR(20);

IF @status = 'ACTIVE'
BEGIN
    PRINT 'User is active';
    -- Multiple statements in BEGIN...END
END
ELSE IF @status = 'PENDING'
    PRINT 'User is pending';  -- Single statement, no BEGIN needed
ELSE
BEGIN
    PRINT 'User is inactive';
END;
CASE 式
SELECT
    employee_id,
    salary,
    CASE
        WHEN salary >= 100000 THEN 'Executive'
        WHEN salary >= 50000 THEN 'Senior'
        WHEN salary >= 30000 THEN 'Mid'
        ELSE 'Junior'
    END AS level
FROM employees;

-- Simple CASE
SELECT
    employee_id,
    CASE status
        WHEN 'A' THEN 'Active'
        WHEN 'I' THEN 'Inactive'
        ELSE 'Unknown'
    END AS status_name
FROM employees;
WHILE ループ
DECLARE @counter INT = 1;

WHILE @counter <= 10
BEGIN
    PRINT 'Counter: ' + CAST(@counter AS VARCHAR);
    SET @counter = @counter + 1;

    IF @counter = 5
        CONTINUE;  -- Skip to next iteration

    IF @counter = 8
        BREAK;  -- Exit loop
END;

エラー処理

TRY...CATCH
BEGIN TRY
    BEGIN TRANSACTION;

    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE id = 2;

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

    -- Error information
    DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
    DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
    DECLARE @ErrorState INT = ERROR_STATE();

    -- Re-throw or log
    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH;
エラー関数
関数説明
ERROR_NUMBER()エラー番号
ERROR_MESSAGE()エラーメッセージ
ERROR_SEVERITY()エラー重大度(0~25)
ERROR_STATE()エラー状態
ERROR_LINE()エラーが発生した行番号
ERROR_PROCEDURE()ストアドプロシージャ名
THROW と RAISERROR の比較
-- THROW (SQL Server 2012+, preferred)
THROW 50001, 'Custom error message', 1;

-- THROW without parameters re-throws current error
BEGIN CATCH
    INSERT INTO error_log (message, error_time)
    VALUES (ERROR_MESSAGE(), GETDATE());

    THROW;  -- Re-throw original error
END CATCH;

-- RAISERROR (legacy)
RAISERROR('Error: %s', 16, 1, @ErrorMessage);

トリガー

DML トリガー
CREATE OR ALTER TRIGGER tr_employees_audit
ON employees
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    SET NOCOUNT ON;

    -- Handle INSERT
    IF EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
    BEGIN
        INSERT INTO employees_audit (action, employee_id, new_salary, changed_by, changed_at)
        SELECT 'INSERT', employee_id, salary, SYSTEM_USER, GETDATE()
        FROM inserted;
    END

    -- Handle UPDATE
    IF EXISTS (SELECT 1 FROM inserted) AND EXISTS (SELECT 1 FROM deleted)
    BEGIN
        INSERT INTO employees_audit (action, employee_id, old_salary, new_salary, changed_by, changed_at)
        SELECT 'UPDATE', i.employee_id, d.salary, i.salary, SYSTEM_USER, GETDATE()
        FROM inserted i
        INNER JOIN deleted d ON i.employee_id = d.employee_id;
    END

    -- Handle DELETE
    IF NOT EXISTS (SELECT 1 FROM inserted) AND EXISTS (SELECT 1 FROM deleted)
    BEGIN
        INSERT INTO employees_audit (action, employee_id, old_salary, changed_by, changed_at)
        SELECT 'DELETE', employee_id, salary, SYSTEM_USER, GETDATE()
        FROM deleted;
    END
END;
GO

トランザクション

BEGIN TRANSACTION;
-- or
BEGIN TRAN;

SAVE TRANSACTION SavePoint1;

-- Rollback to savepoint
ROLLBACK TRANSACTION SavePoint1;

COMMIT TRANSACTION;
-- or
COMMIT;

-- Check transaction count
SELECT @@TRANCOUNT;

-- Named transaction
BEGIN TRANSACTION MyTransaction;
COMMIT TRANSACTION MyTransaction;

共通テーブル式(CTE)

;WITH dept_stats AS (
    SELECT
        department_id,
        COUNT(*) AS emp_count,
        AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department_id
)
SELECT d.department_name, ds.emp_count, ds.avg_salary
FROM departments d
INNER JOIN dept_stats ds ON d.department_id = ds.department_id;

ベストプラクティス

DO
  • プロシージャで SET NOCOUNT ON を使用する
  • エラー処理に TRY...CATCH を使用する
  • 動的 SQL には sp_executesql を使用する
  • 可能な場合は FAST_FORWARD カーソルを使用する
  • 一括操作にはテーブル値パラメーターを使用する
  • オブジェクト名には必ずスキーマを指定する
DON'T
  • 本番コードで SELECT * を使用しない
  • 文字列連結で動的 SQL を構築しない
  • セットベースの操作で対応できる場合はカーソルを使用しない
  • エラー処理を無視しない
  • 非推奨の機能(GROUP BY ALL など)を使用しない

このスキルを使用しない場合

  • 基本的な SQL Server SQL - データ型、インデックス、テンポラルテーブルには sqlserver スキルを使用してください
  • PL/pgSQL(PostgreSQL) - PostgreSQL のプロシージャには plpgsql スキルを使用してください
  • PL/SQL(Oracle) - Oracle のプロシージャには plsql スキルを使用してください
  • 基本的な SQL - ANSI SQL の基本については sql-fundamentals を使用してください

アンチパターン

アンチパターン問題解決策
TRY...CATCH を使用しないエラーが気づかれないエラー処理ブロックを追加する
RAISERROR を使用する非推奨のパターンTHROW を使用する(SQL 2012 以降)
SET NOCOUNT ON がないパフォーマンスのオーバーヘッドすべてのプロシージャに追加する
動的 SQL の文字列連結SQL インジェクションパラメーターを指定して sp_executesql を使用する
TOP に ORDER BY を指定せず SELECT を使用する非決定的な結果常に ORDER BY を指定する
セットベースで対応できるのにカーソルを使用するパフォーマンスの低下セット演算に書き換える

すばやいトラブルシューティング

問題診断修正
トランザクションが未コミットSELECT @@TRANCOUNTCOMMIT または ROLLBACK を追加する
プロシージャの実行が遅いSSMS の実行プランインデックスを追加し、クエリを書き換える
エラーが握りつぶされるCATCH ブロックを確認するTHROW またはログ記録が行われるようにする
デッドロックの被害者拡張イベント一貫したアクセス順序にする
パラメータースニッフィングプランを比較するOPTION (RECOMPILE) またはローカル変数を使用する

リファレンスドキュメント

コメント