Agent skill

sql-server-admin

SQL Server administration and maintenance. Use for database backups, security, user management, maintenance tasks, monitoring, and troubleshooting.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/sql-server-admin

SKILL.md

SQL Server Administration Skill

Expert knowledge for SQL Server database administration, maintenance, security, and operational tasks.

Database Management

Create Database

sql
CREATE DATABASE MyDatabase
ON PRIMARY (
    NAME = 'MyDatabase_Data',
    FILENAME = '/var/opt/mssql/data/MyDatabase.mdf',
    SIZE = 100MB,
    MAXSIZE = 1GB,
    FILEGROWTH = 10MB
)
LOG ON (
    NAME = 'MyDatabase_Log',
    FILENAME = '/var/opt/mssql/data/MyDatabase_log.ldf',
    SIZE = 50MB,
    MAXSIZE = 500MB,
    FILEGROWTH = 5MB
);

Alter Database

sql
-- Change database name
ALTER DATABASE OldName MODIFY NAME = NewName;

-- Set recovery model
ALTER DATABASE MyDatabase SET RECOVERY FULL;
-- Options: SIMPLE, FULL, BULK_LOGGED

-- Set to single-user mode
ALTER DATABASE MyDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE;

-- Set back to multi-user
ALTER DATABASE MyDatabase SET MULTI_USER;

-- Enable snapshot isolation
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON;

Drop Database

sql
-- Drop database (must not be in use)
DROP DATABASE MyDatabase;

-- Force drop (disconnect users first)
ALTER DATABASE MyDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE MyDatabase;

Database Information

sql
-- List all databases
SELECT
    name,
    database_id,
    create_date,
    state_desc,
    recovery_model_desc
FROM sys.databases
ORDER BY name;

-- Database size
EXEC sp_spaceused;

-- Database file information
SELECT
    name,
    physical_name,
    size * 8 / 1024 AS SizeMB,
    max_size
FROM sys.database_files;

Backup and Restore

Full Backup

sql
-- Full database backup
BACKUP DATABASE MyDatabase
TO DISK = '/var/opt/mssql/backup/MyDatabase_Full.bak'
WITH FORMAT, COMPRESSION, STATS = 10;

-- Full backup with name and description
BACKUP DATABASE MyDatabase
TO DISK = '/var/opt/mssql/backup/MyDatabase_Full_20241220.bak'
WITH
    FORMAT,
    COMPRESSION,
    NAME = 'MyDatabase Full Backup',
    DESCRIPTION = 'Full backup performed on 2024-12-20',
    STATS = 10;

Differential Backup

sql
-- Differential backup (changes since last full backup)
BACKUP DATABASE MyDatabase
TO DISK = '/var/opt/mssql/backup/MyDatabase_Diff.bak'
WITH DIFFERENTIAL, FORMAT, COMPRESSION;

Transaction Log Backup

sql
-- Transaction log backup (requires FULL recovery model)
BACKUP LOG MyDatabase
TO DISK = '/var/opt/mssql/backup/MyDatabase_Log.trn'
WITH FORMAT, COMPRESSION;

Restore Database

sql
-- View backup file contents
RESTORE FILELISTONLY
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Full.bak';

-- Restore with replace
RESTORE DATABASE MyDatabase
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Full.bak'
WITH REPLACE,
    MOVE 'MyDatabase_Data' TO '/var/opt/mssql/data/MyDatabase.mdf',
    MOVE 'MyDatabase_Log' TO '/var/opt/mssql/data/MyDatabase_log.ldf';

-- Restore to different database name
RESTORE DATABASE MyDatabase_Copy
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Full.bak'
WITH
    MOVE 'MyDatabase_Data' TO '/var/opt/mssql/data/MyDatabase_Copy.mdf',
    MOVE 'MyDatabase_Log' TO '/var/opt/mssql/data/MyDatabase_Copy_log.ldf';

-- Restore with recovery/norecovery
RESTORE DATABASE MyDatabase
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Full.bak'
WITH NORECOVERY;  -- To apply more backups (diff/log)

-- Apply differential backup
RESTORE DATABASE MyDatabase
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Diff.bak'
WITH NORECOVERY;

-- Apply log backup and bring online
RESTORE LOG MyDatabase
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Log.trn'
WITH RECOVERY;  -- Brings database online

Point-in-Time Restore

sql
-- Restore to specific point in time
RESTORE DATABASE MyDatabase
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Full.bak'
WITH NORECOVERY;

RESTORE LOG MyDatabase
FROM DISK = '/var/opt/mssql/backup/MyDatabase_Log.trn'
WITH STOPAT = '2024-12-20 14:30:00', RECOVERY;

User and Security Management

Create Login

sql
-- SQL Server authentication
CREATE LOGIN john_doe
WITH PASSWORD = 'StrongP@ssw0rd!';

-- Windows authentication
CREATE LOGIN [DOMAIN\username]
FROM WINDOWS;

Create User

sql
-- Create database user from login
USE MyDatabase;
CREATE USER john_doe FOR LOGIN john_doe;

-- Create user without login (for contained databases)
CREATE USER app_user WITH PASSWORD = 'StrongP@ssw0rd!';

Grant Permissions

sql
-- Grant database role membership
ALTER ROLE db_datareader ADD MEMBER john_doe;
ALTER ROLE db_datawriter ADD MEMBER john_doe;

-- Grant specific permissions
GRANT SELECT, INSERT, UPDATE ON dbo.Orders TO john_doe;
GRANT EXECUTE ON dbo.GetCustomerOrders TO john_doe;

-- Grant schema permissions
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::Sales TO john_doe;

-- Grant database-level permissions
GRANT CREATE TABLE TO john_doe;
GRANT VIEW DATABASE STATE TO monitoring_user;

Revoke Permissions

sql
REVOKE SELECT ON dbo.Orders FROM john_doe;
REVOKE EXECUTE ON dbo.GetCustomerOrders FROM john_doe;

Deny Permissions

sql
-- Explicitly deny (overrides grants)
DENY DELETE ON dbo.Customers TO john_doe;

Database Roles

sql
-- Create custom role
CREATE ROLE SalesTeam;

-- Grant permissions to role
GRANT SELECT, INSERT, UPDATE ON SCHEMA::Sales TO SalesTeam;

-- Add users to role
ALTER ROLE SalesTeam ADD MEMBER john_doe;

-- Built-in database roles:
-- db_owner       - Full control
-- db_datareader  - SELECT on all tables
-- db_datawriter  - INSERT, UPDATE, DELETE on all tables
-- db_ddladmin    - CREATE, ALTER, DROP objects
-- db_backupoperator - Backup operations

View Permissions

sql
-- User's permissions
SELECT * FROM fn_my_permissions(NULL, 'DATABASE');

-- Specific user's permissions
EXECUTE AS USER = 'john_doe';
SELECT * FROM fn_my_permissions(NULL, 'DATABASE');
REVERT;

-- User's role memberships
SELECT
    USER_NAME(rm.member_principal_id) AS UserName,
    USER_NAME(rm.role_principal_id) AS RoleName
FROM sys.database_role_members rm;

Index Maintenance

Check Index Fragmentation

sql
SELECT
    OBJECT_NAME(ips.object_id) AS TableName,
    i.name AS IndexName,
    ips.index_type_desc,
    ips.avg_fragmentation_in_percent,
    ips.page_count,
    CASE
        WHEN ips.avg_fragmentation_in_percent < 10 THEN 'No action needed'
        WHEN ips.avg_fragmentation_in_percent < 30 THEN 'Reorganize'
        ELSE 'Rebuild'
    END AS Recommendation
FROM sys.dm_db_index_physical_stats(
    DB_ID(), NULL, NULL, NULL, 'LIMITED'
) ips
INNER JOIN sys.indexes i
    ON ips.object_id = i.object_id
    AND ips.index_id = i.index_id
WHERE ips.page_count > 1000  -- Only indexes with significant pages
ORDER BY ips.avg_fragmentation_in_percent DESC;

Rebuild Indexes

sql
-- Rebuild single index
ALTER INDEX IX_Orders_CustomerId ON Orders REBUILD;

-- Rebuild all indexes on table
ALTER INDEX ALL ON Orders REBUILD;

-- Rebuild with options
ALTER INDEX IX_Orders_CustomerId ON Orders
REBUILD WITH (
    ONLINE = ON,          -- Enterprise Edition only
    MAXDOP = 4,           -- Parallel processing
    SORT_IN_TEMPDB = ON   -- Use tempdb for sorting
);

Reorganize Indexes

sql
-- Reorganize index (online operation)
ALTER INDEX IX_Orders_CustomerId ON Orders REORGANIZE;

-- Reorganize with LOB compaction
ALTER INDEX IX_Orders_CustomerId ON Orders
REORGANIZE WITH (LOB_COMPACTION = ON);

Update Statistics

sql
-- Update statistics for table
UPDATE STATISTICS Orders;

-- Update with full scan
UPDATE STATISTICS Orders WITH FULLSCAN;

-- Update specific index statistics
UPDATE STATISTICS Orders IX_Orders_CustomerId WITH FULLSCAN;

-- Update all statistics in database
EXEC sp_updatestats;

Maintenance Plan Script

sql
-- Comprehensive maintenance script
DECLARE @SQL NVARCHAR(MAX);

-- Rebuild fragmented indexes (>30%)
DECLARE index_cursor CURSOR FOR
SELECT
    'ALTER INDEX ' + QUOTENAME(i.name) +
    ' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + '.' + QUOTENAME(OBJECT_NAME(i.object_id)) +
    ' REBUILD;'
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 30
    AND ips.page_count > 1000
    AND i.name IS NOT NULL;

OPEN index_cursor;
FETCH NEXT FROM index_cursor INTO @SQL;

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @SQL;
    EXEC sp_executesql @SQL;
    FETCH NEXT FROM index_cursor INTO @SQL;
END;

CLOSE index_cursor;
DEALLOCATE index_cursor;

-- Update statistics
EXEC sp_updatestats;

Monitoring and Performance

Active Sessions

sql
SELECT
    session_id,
    login_name,
    host_name,
    program_name,
    status,
    cpu_time,
    total_elapsed_time / 1000 AS elapsed_seconds,
    reads,
    writes,
    last_request_start_time
FROM sys.dm_exec_sessions
WHERE is_user_process = 1
ORDER BY cpu_time DESC;

Currently Running Queries

sql
SELECT
    r.session_id,
    r.status,
    r.command,
    r.cpu_time,
    r.total_elapsed_time / 1000 AS elapsed_seconds,
    r.reads,
    r.writes,
    r.blocking_session_id,
    SUBSTRING(qt.text, (r.statement_start_offset/2)+1,
        ((CASE r.statement_end_offset
            WHEN -1 THEN DATALENGTH(qt.text)
            ELSE r.statement_end_offset
        END - r.statement_start_offset)/2) + 1) AS query_text
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) qt
WHERE r.session_id <> @@SPID  -- Exclude current session
ORDER BY r.total_elapsed_time DESC;

Blocking

sql
-- Find blocking chains
SELECT
    blocked.session_id AS blocked_session_id,
    blocked_sql.text AS blocked_query,
    blocker.session_id AS blocker_session_id,
    blocker_sql.text AS blocker_query,
    waits.wait_type,
    waits.wait_time_ms
FROM sys.dm_exec_requests blocked
INNER JOIN sys.dm_exec_requests blocker
    ON blocked.blocking_session_id = blocker.session_id
CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle) blocked_sql
CROSS APPLY sys.dm_exec_sql_text(blocker.sql_handle) blocker_sql
LEFT JOIN sys.dm_os_waiting_tasks waits
    ON blocked.session_id = waits.session_id;

Kill Session

sql
-- Kill blocking session
KILL 53;  -- session_id

-- Kill with rollback status
KILL 53 WITH STATUSONLY;

Wait Statistics

sql
SELECT TOP 20
    wait_type,
    wait_time_ms / 1000 AS wait_time_seconds,
    (wait_time_ms * 100.0) / SUM(wait_time_ms) OVER() AS percentage,
    waiting_tasks_count
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
    -- Filter out benign waits
    'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE',
    'SLEEP_TASK', 'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH',
    'WAITFOR', 'LOGMGR_QUEUE', 'CHECKPOINT_QUEUE',
    'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BROKER_TO_FLUSH',
    'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',
    'DISPATCHER_QUEUE_SEMAPHORE', 'FT_IFTS_SCHEDULER_IDLE_WAIT',
    'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP'
)
ORDER BY wait_time_ms DESC;

Database Size Growth

sql
SELECT
    DB_NAME() AS database_name,
    name AS file_name,
    type_desc,
    physical_name,
    size * 8 / 1024 AS size_mb,
    (size * 8 / 1024) - (FILEPROPERTY(name, 'SpaceUsed') * 8 / 1024) AS free_space_mb,
    CAST((FILEPROPERTY(name, 'SpaceUsed') * 100.0 / size) AS DECIMAL(5,2)) AS percent_used
FROM sys.database_files;

Troubleshooting

Check Error Log

sql
-- Read SQL Server error log
EXEC sp_readerrorlog;

-- Read specific error log
EXEC sp_readerrorlog 0;  -- Current log
EXEC sp_readerrorlog 1;  -- Previous log

-- Search for specific text
EXEC sp_readerrorlog 0, 1, N'error';

Deadlocks

sql
-- Enable trace flag for deadlock capture
DBCC TRACEON(1222, -1);  -- Global

-- Read captured deadlocks from error log
EXEC sp_readerrorlog 0, 1, N'deadlock';

-- Turn off trace flag
DBCC TRACEOFF(1222, -1);

DBCC Commands

sql
-- Check database integrity
DBCC CHECKDB('MyDatabase') WITH NO_INFOMSGS;

-- Check specific table
DBCC CHECKTABLE('Orders') WITH NO_INFOMSGS;

-- Update usage stats
DBCC UPDATEUSAGE('MyDatabase');

-- Free procedure cache
DBCC FREEPROCCACHE;

-- Clear wait stats (useful after solving issues)
DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Shrink database (avoid in production!)
DBCC SHRINKDATABASE('MyDatabase', 10);  -- 10% free space

-- Shrink file
DBCC SHRINKFILE('MyDatabase_Log', 50);  -- MB

sqlcmd Usage

Connect to SQL Server

bash
# Connect with SQL authentication
sqlcmd -S localhost -U sa -P 'YourPassword'

# Connect with Windows authentication (if supported)
sqlcmd -S localhost -E

# Connect to specific database
sqlcmd -S localhost -U sa -P 'YourPassword' -d MyDatabase

# Execute query from command line
sqlcmd -S localhost -U sa -P 'YourPassword' -Q "SELECT @@VERSION"

# Execute script file
sqlcmd -S localhost -U sa -P 'YourPassword' -i script.sql

# Output to file
sqlcmd -S localhost -U sa -P 'YourPassword' -Q "SELECT * FROM Users" -o output.txt

# Use variable
sqlcmd -S localhost -U sa -P 'YourPassword' -v MyVar=Value -i script.sql

sqlcmd Commands

Within sqlcmd session:

sql
-- List databases
SELECT name FROM sys.databases;
GO

-- Change database
USE MyDatabase;
GO

-- Execute script
:r script.sql

-- Set variable
:setvar MyVar "MyValue"
SELECT '$(MyVar)';
GO

-- Quit
EXIT
-- or
QUIT

Useful Queries

Table Sizes

sql
SELECT
    OBJECT_SCHEMA_NAME(p.object_id) AS SchemaName,
    OBJECT_NAME(p.object_id) AS TableName,
    SUM(a.total_pages) * 8 / 1024 AS TotalSpaceMB,
    SUM(a.used_pages) * 8 / 1024 AS UsedSpaceMB,
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 / 1024 AS UnusedSpaceMB,
    SUM(p.rows) AS RowCount
FROM sys.partitions p
INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE OBJECT_SCHEMA_NAME(p.object_id) <> 'sys'
GROUP BY p.object_id
ORDER BY TotalSpaceMB DESC;

Index Usage

sql
SELECT
    OBJECT_NAME(s.object_id) AS TableName,
    i.name AS IndexName,
    s.user_seeks,
    s.user_scans,
    s.user_lookups,
    s.user_updates,
    s.last_user_seek,
    s.last_user_scan
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE database_id = DB_ID()
    AND OBJECTPROPERTY(s.object_id, 'IsUserTable') = 1
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC;

When to Use This Skill

Use this skill when:

  • Managing databases (create, backup, restore)
  • Setting up security and users
  • Performing maintenance tasks
  • Troubleshooting performance issues
  • Monitoring database health
  • Using sqlcmd for database operations
  • Managing indexes and statistics

Simply mention database administration, maintenance, backups, users, or monitoring, and this knowledge will be applied.

Didn't find tool you were looking for?

Be as detailed as possible for better results