alexsusanu@docs:SQL Server LIMS Monitoring Scripts $
alexsusanu@docs
:~$ cat SQL Server LIMS Monitoring Scripts.md

HomeORACLE → SQL Server LIMS Monitoring Scripts

SQL Server LIMS Monitoring Scripts

PowerShell scripts for SQL Server performance monitoring in LIMS environments

region SQL Server Connection and Health

1. SQL Server connection tester with detailed diagnostics

function Test-SQLServerForLIMS {
    param(
        [string]$ServerName = "localhost",
        [string]$DatabaseName = "LIMS",
        [string]$Username,
        [string]$Password,
        [switch]$IntegratedSecurity = $true,
        [int]$TimeoutSeconds = 30
    )

    # Build connection string
    if ($IntegratedSecurity) {
        $connectionString = "Server=$ServerName;Database=$DatabaseName;Integrated Security=true;Connection Timeout=$TimeoutSeconds;"
    } else {
        $connectionString = "Server=$ServerName;Database=$DatabaseName;User Id=$Username;Password=$Password;Connection Timeout=$TimeoutSeconds;"
    }

    Write-Host "Testing SQL Server connection for LIMS..." -ForegroundColor Yellow
    Write-Host "Server: $ServerName" -ForegroundColor Gray
    Write-Host "Database: $DatabaseName" -ForegroundColor Gray

    try {
        # Test basic connectivity
        $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
        $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
        $connection.Open()

        # Get server information
        $command = New-Object System.Data.SqlClient.SqlCommand("SELECT @@VERSION, @@SERVERNAME, DB_NAME(), SYSTEM_USER, GETDATE()", $connection)
        $reader = $command.ExecuteReader()

        $serverInfo = @{}
        if ($reader.Read()) {
            $serverInfo = @{
                Version = $reader[0].ToString()
                ServerName = $reader[1].ToString()
                Database = $reader[2].ToString()
                User = $reader[3].ToString()
                ServerTime = $reader[4]
            }
        }
        $reader.Close()

        # Test a simple query performance
        $testCommand = New-Object System.Data.SqlClient.SqlCommand("SELECT COUNT(*) FROM sys.tables", $connection)
        $testResult = $testCommand.ExecuteScalar()

        $stopwatch.Stop()
        $connection.Close()

        $result = [PSCustomObject]@{
            Status = "Success"
            Server = $ServerName
            Database = $DatabaseName
            ResponseTimeMs = $stopwatch.ElapsedMilliseconds
            SqlServerVersion = $serverInfo.Version.Split("`n")[0]
            ServerName = $serverInfo.ServerName
            ConnectedAs = $serverInfo.User
            ServerTime = $serverInfo.ServerTime
            TableCount = $testResult
            Timestamp = Get-Date
        }

        Write-Host "✓ Connection successful" -ForegroundColor Green
        Write-Host "Response time: $($stopwatch.ElapsedMilliseconds)ms" -ForegroundColor Gray
        Write-Host "Connected as: $($serverInfo.User)" -ForegroundColor Gray
        Write-Host "Server time: $($serverInfo.ServerTime)" -ForegroundColor Gray

        return $result
    }
    catch {
        Write-Host "✗ Connection failed" -ForegroundColor Red
        Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red

        # Provide specific guidance for common errors
        if ($_.Exception.Message -like "*login failed*") {
            Write-Host "Hint: Check username/password or SQL Server authentication mode" -ForegroundColor Yellow
        }
        elseif ($_.Exception.Message -like "*server was not found*") {
            Write-Host "Hint: Check server name, SQL Server service status, and firewall" -ForegroundColor Yellow
        }
        elseif ($_.Exception.Message -like "*timeout*") {
            Write-Host "Hint: Check network connectivity and SQL Server responsiveness" -ForegroundColor Yellow
        }

        return [PSCustomObject]@{
            Status = "Failed"
            Server = $ServerName
            Database = $DatabaseName
            Error = $_.Exception.Message
            Timestamp = Get-Date
        }
    }
}

2. SQL Server performance monitoring for LIMS

function Get-SQLServerLIMSPerformance {
    param(
        [string]$ServerName = "localhost",
        [string]$DatabaseName = "LIMS",
        [string]$ConnectionString
    )

    if (-not $ConnectionString) {
        $ConnectionString = "Server=$ServerName;Database=$DatabaseName;Integrated Security=true;Connection Timeout=30;"
    }

    $performanceQueries = @{
        "DatabaseInfo" = @"
SELECT 
    name AS DatabaseName,
    database_id,
    create_date,
    state_desc AS Status,
    recovery_model_desc AS RecoveryModel,
    page_verify_option_desc AS PageVerifyOption
FROM sys.databases 
WHERE name = '$DatabaseName'
"@

        "DatabaseSize" = @"
SELECT 
    type_desc AS FileType,
    name AS LogicalName,
    physical_name AS PhysicalName,
    CAST(size/128.0 AS DECIMAL(12,2)) AS SizeMB,
    CAST(FILEPROPERTY(name,'SpaceUsed')/128.0 AS DECIMAL(12,2)) AS UsedMB,
    CAST((size-FILEPROPERTY(name,'SpaceUsed'))/128.0 AS DECIMAL(12,2)) AS FreeMB,
    CAST((FILEPROPERTY(name,'SpaceUsed')*100.0)/size AS DECIMAL(5,2)) AS PercentUsed
FROM sys.database_files
"@

        "ActiveConnections" = @"
SELECT 
    program_name,
    host_name,
    login_name,
    COUNT(*) AS ConnectionCount,
    COUNT(CASE WHEN status = 'running' THEN 1 END) AS ActiveCount,
    COUNT(CASE WHEN status = 'sleeping' THEN 1 END) AS IdleCount
FROM sys.dm_exec_sessions 
WHERE is_user_process = 1
AND database_id = DB_ID('$DatabaseName')
GROUP BY program_name, host_name, login_name
ORDER BY ConnectionCount DESC
"@

        "WaitStats" = @"
SELECT TOP 10
    wait_type,
    waiting_tasks_count,
    wait_time_ms,
    max_wait_time_ms,
    signal_wait_time_ms,
    CAST(wait_time_ms / 1000.0 AS DECIMAL(12,2)) AS wait_time_seconds,
    CAST(100.0 * wait_time_ms / SUM(wait_time_ms) OVER() AS DECIMAL(5,2)) AS percentage
FROM sys.dm_os_wait_stats 
WHERE wait_type NOT IN ('CLR_SEMAPHORE','LAZYWRITER_SLEEP','RESOURCE_QUEUE','SLEEP_TASK',
    'SLEEP_SYSTEMTASK','SQLTRACE_WAIT','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')
AND wait_time_ms > 0
ORDER BY wait_time_ms DESC
"@

        "BlockingSessions" = @"
SELECT 
    r.session_id AS blocked_session_id,
    r.blocking_session_id,
    s.login_name AS blocked_user,
    s.host_name AS blocked_machine,
    s.program_name AS blocked_program,
    bs.login_name AS blocking_user,
    bs.host_name AS blocking_machine,
    bs.program_name AS blocking_program,
    r.wait_type,
    r.wait_time,
    r.last_wait_type,
    st.text AS sql_text
FROM sys.dm_exec_requests r
INNER JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
LEFT JOIN sys.dm_exec_sessions bs ON r.blocking_session_id = bs.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
WHERE r.blocking_session_id > 0
"@

        "LongRunningQueries" = @"
SELECT 
    r.session_id,
    s.login_name,
    s.host_name,
    s.program_name,
    r.status,
    r.command,
    r.start_time,
    DATEDIFF(minute, r.start_time, GETDATE()) AS runtime_minutes,
    r.cpu_time,
    r.logical_reads,
    r.writes,
    st.text AS sql_text,
    qp.query_plan
FROM sys.dm_exec_requests r
INNER JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
OUTER APPLY sys.dm_exec_query_plan(r.plan_handle) qp
WHERE r.session_id > 50
AND DATEDIFF(minute, r.start_time, GETDATE()) > 5
ORDER BY runtime_minutes DESC
"@

        "TopQueries" = @"
SELECT TOP 10
    qs.execution_count,
    CAST(qs.total_elapsed_time / 1000000.0 AS DECIMAL(12,2)) AS total_elapsed_seconds,
    CAST(qs.total_elapsed_time / qs.execution_count / 1000000.0 AS DECIMAL(12,4)) AS avg_elapsed_seconds,
    CAST(qs.total_cpu_time / 1000000.0 AS DECIMAL(12,2)) AS total_cpu_seconds,
    qs.total_logical_reads,
    qs.total_logical_writes,
    qs.creation_time,
    qs.last_execution_time,
    SUBSTRING(st.text, (qs.statement_start_offset/2)+1, 
        ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) 
        ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) + 1) AS statement_text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
WHERE st.text NOT LIKE '%sys.dm_%'
ORDER BY qs.total_elapsed_time DESC
"@
    }

    $results = @{}

    Write-Host "Gathering SQL Server performance data for LIMS..." -ForegroundColor Yellow

    try {
        $connection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString)
        $connection.Open()

        foreach ($query in $performanceQueries.GetEnumerator()) {
            Write-Host "Collecting $($query.Key)..." -ForegroundColor Cyan

            try {
                $command = New-Object System.Data.SqlClient.SqlCommand($query.Value, $connection)
                $adapter = New-Object System.Data.SqlClient.SqlDataAdapter($command)
                $dataset = New-Object System.Data.DataSet
                $adapter.Fill($dataset) | Out-Null

                $results[$query.Key] = $dataset.Tables[0]
            }
            catch {
                Write-Warning "Failed to execute $($query.Key): $($_.Exception.Message)"
                $results[$query.Key] = $null
            }
        }

        $connection.Close()

        # Display key findings
        Write-Host "`nSQL Server Performance Summary:" -ForegroundColor Green

        # Database size
        if ($results["DatabaseSize"]) {
            $totalSizeMB = ($results["DatabaseSize"] | Measure-Object SizeMB -Sum).Sum
            $totalUsedMB = ($results["DatabaseSize"] | Measure-Object UsedMB -Sum).Sum
            $usedPercent = [math]::Round(($totalUsedMB / $totalSizeMB) * 100, 1)
            Write-Host "Database size: $([math]::Round($totalSizeMB,1))MB ($usedPercent% used)" -ForegroundColor Cyan
        }

        # Active connections
        if ($results["ActiveConnections"]) {
            $totalConnections = ($results["ActiveConnections"] | Measure-Object ConnectionCount -Sum).Sum
            $activeConnections = ($results["ActiveConnections"] | Measure-Object ActiveCount -Sum).Sum
            Write-Host "Connections: $totalConnections total, $activeConnections active" -ForegroundColor Cyan
        }

        # Blocking sessions
        if ($results["BlockingSessions"] -and $results["BlockingSessions"].Rows.Count -gt 0) {
            Write-Host "⚠ WARNING: $($results["BlockingSessions"].Rows.Count) blocked sessions detected!" -ForegroundColor Red
        } else {
            Write-Host "✓ No blocking sessions detected" -ForegroundColor Green
        }

        # Long running queries
        if ($results["LongRunningQueries"] -and $results["LongRunningQueries"].Rows.Count -gt 0) {
            $maxRuntime = ($results["LongRunningQueries"] | Measure-Object runtime_minutes -Maximum).Maximum
            Write-Host "⚠ WARNING: $($results["LongRunningQueries"].Rows.Count) long-running queries (max: $maxRuntime minutes)" -ForegroundColor Yellow
        } else {
            Write-Host "✓ No long-running queries detected" -ForegroundColor Green
        }

        return $results
    }
    catch {
        Write-Error "Failed to gather SQL Server performance data: $($_.Exception.Message)"
        return $null
    }
}

3. Generate SQL Server health check script

function New-SQLServerLIMSHealthScript {
    param(
        [string]$OutputPath = "C:\temp\sql_server_lims_health.sql",
        [string]$DatabaseName = "LIMS"
    )

    $sqlScript = @"
-- SQL Server LIMS Health Check Script
-- Generated: $(Get-Date)
-- Database: $DatabaseName

SET NOCOUNT ON
PRINT '================================================'
PRINT 'SQL SERVER LIMS HEALTH CHECK - ' + CONVERT(varchar, GETDATE(), 120)
PRINT '================================================'

PRINT ''
PRINT '1. SERVER INFORMATION'
SELECT 
    @@SERVERNAME AS ServerName,
    @@VERSION AS Version,
    SERVERPROPERTY('ProductLevel') AS ServicePack,
    SERVERPROPERTY('Edition') AS Edition,
    GETDATE() AS CurrentTime

PRINT ''
PRINT '2. DATABASE STATUS'
SELECT 
    name AS DatabaseName,
    state_desc AS Status,
    recovery_model_desc AS RecoveryModel,
    CAST(size*8.0/1024 AS DECIMAL(10,2)) AS SizeMB
FROM sys.databases 
WHERE name = '$DatabaseName'

PRINT ''
PRINT '3. DATABASE FILE USAGE'
USE [$DatabaseName]
SELECT 
    type_desc AS FileType,
    name AS LogicalName,
    CAST(size/128.0 AS DECIMAL(12,2)) AS SizeMB,
    CAST(FILEPROPERTY(name,'SpaceUsed')/128.0 AS DECIMAL(12,2)) AS UsedMB,
    CAST((size-FILEPROPERTY(name,'SpaceUsed'))/128.0 AS DECIMAL(12,2)) AS FreeMB,
    CAST((FILEPROPERTY(name,'SpaceUsed')*100.0)/size AS DECIMAL(5,2)) AS PercentUsed
FROM sys.database_files

PRINT ''
PRINT '4. ACTIVE CONNECTIONS'
SELECT 
    program_name,
    login_name,
    COUNT(*) AS ConnectionCount,
    COUNT(CASE WHEN status = 'running' THEN 1 END) AS ActiveCount
FROM sys.dm_exec_sessions 
WHERE is_user_process = 1
AND database_id = DB_ID('$DatabaseName')
GROUP BY program_name, login_name
HAVING COUNT(*) > 2
ORDER BY ConnectionCount DESC

PRINT ''
PRINT '5. BLOCKING SESSIONS (CRITICAL ISSUE IF ANY)'
IF EXISTS (SELECT 1 FROM sys.dm_exec_requests WHERE blocking_session_id > 0)
BEGIN
    PRINT '*** BLOCKING DETECTED ***'
    SELECT 
        r.session_id AS BlockedSession,
        r.blocking_session_id AS BlockingSession,
        s.login_name AS BlockedUser,
        bs.login_name AS BlockingUser,
        r.wait_type,
        r.wait_time,
        SUBSTRING(st.text, 1, 200) AS SQLText
    FROM sys.dm_exec_requests r
    INNER JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
    LEFT JOIN sys.dm_exec_sessions bs ON r.blocking_session_id = bs.session_id
    CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
    WHERE r.blocking_session_id > 0
END
ELSE
    PRINT 'No blocking sessions detected'

PRINT ''
PRINT '6. LONG RUNNING QUERIES (>5 MINUTES)'
IF EXISTS (SELECT 1 FROM sys.dm_exec_requests r INNER JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id WHERE r.session_id > 50 AND DATEDIFF(minute, r.start_time, GETDATE()) > 5)
BEGIN
    SELECT 
        r.session_id,
        s.login_name,
        r.status,
        r.start_time,
        DATEDIFF(minute, r.start_time, GETDATE()) AS RuntimeMinutes,
        SUBSTRING(st.text, 1, 200) AS SQLText
    FROM sys.dm_exec_requests r
    INNER JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
    CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
    WHERE r.session_id > 50
    AND DATEDIFF(minute, r.start_time, GETDATE()) > 5
    ORDER BY RuntimeMinutes DESC
END
ELSE
    PRINT 'No long running queries detected'

PRINT ''
PRINT '7. TOP WAIT STATISTICS'
SELECT TOP 10
    wait_type,
    waiting_tasks_count,
    CAST(wait_time_ms / 1000.0 AS DECIMAL(12,2)) AS WaitTimeSeconds,
    CAST(100.0 * wait_time_ms / SUM(wait_time_ms) OVER() AS DECIMAL(5,2)) AS Percentage
FROM sys.dm_os_wait_stats 
WHERE wait_type NOT IN ('CLR_SEMAPHORE','LAZYWRITER_SLEEP','RESOURCE_QUEUE','SLEEP_TASK',
    'SLEEP_SYSTEMTASK','SQLTRACE_WAIT','WAITFOR', 'LOGMGR_QUEUE','CHECKPOINT_QUEUE',
    'REQUEST_FOR_DEADLOCK_SEARCH','XE_TIMER_EVENT','BROKER_TO_FLUSH','BROKER_TASK_STOP')
AND wait_time_ms > 1000
ORDER BY wait_time_ms DESC

PRINT ''
PRINT '8. ERROR LOG SUMMARY (RECENT ERRORS)'
EXEC xp_readerrorlog 0, 1, 'error'
EXEC xp_readerrorlog 0, 1, 'fail'
EXEC xp_readerrorlog 0, 1, 'timeout'

PRINT ''
PRINT '9. LIMS-SPECIFIC CHECKS'
-- Check for tables with no recent activity (potential issues)
SELECT 
    t.name AS TableName,
    p.rows AS RowCount,
    i.last_user_update,
    DATEDIFF(day, i.last_user_update, GETDATE()) AS DaysSinceUpdate
FROM sys.tables t
INNER JOIN sys.dm_db_partition_stats p ON t.object_id = p.object_id AND p.index_id IN (0,1)
LEFT JOIN sys.dm_db_index_usage_stats i ON t.object_id = i.object_id AND i.index_id IN (0,1)
WHERE t.name LIKE '%sample%' OR t.name LIKE '%test%' OR t.name LIKE '%result%'
ORDER BY DaysSinceUpdate DESC

PRINT ''
PRINT 'Health check complete. Review any warnings or errors above.'
PRINT 'Timestamp: ' + CONVERT(varchar, GETDATE(), 120)
"@

    $sqlScript | Out-File -FilePath $OutputPath -Encoding UTF8
    Write-Host "SQL Server LIMS health check script created: $OutputPath" -ForegroundColor Green
    Write-Host "Run this in SSMS: File -> Open -> $OutputPath" -ForegroundColor Cyan
    Write-Host "Or from command line: sqlcmd -S servername -d $DatabaseName -i `"$OutputPath`"" -ForegroundColor Cyan
}

endregion

Usage examples

Write-Host @"

SQL SERVER LIMS MONITORING COMMANDS:

  1. Test SQL Server connection:
    Test-SQLServerForLIMS -ServerName "SQLSERVER01" -DatabaseName "LIMS_PROD"

  2. Get comprehensive performance data:
    $perf = Get-SQLServerLIMSPerformance -ServerName "SQLSERVER01" -DatabaseName "LIMS_PROD"$perf["BlockingSessions"] # View blocking sessions
    `$perf["LongRunningQueries"] # View slow queries

  3. Generate health check script:
    New-SQLServerLIMSHealthScript -DatabaseName "LIMS_PROD"

  4. With SQL authentication:
    Test-SQLServerForLIMS -ServerName "SERVER" -DatabaseName "LIMS" -Username "lims_user" -Password "password" -IntegratedSecurity:`$false

"@ -ForegroundColor Green

Last updated: 2025-08-26 20:00 UTC