Sunday, March 29, 2020

Get top total logical reads queries

Optimizing SQL Server Performance: Identifying Resource-Intensive Queries

Ever wonder which queries are hogging your SQL Server's resources? This handy SQL script helps you pinpoint the culprits by showing you the top 50 most resource-intensive queries based on logical reads. It's a fantastic way to quickly identify areas for optimization and improve your database's overall performance.

Here's the SQL query:

SELECT TOP (50) DB_NAME(t.[dbid]) AS [Database Name]
 ,REPLACE(REPLACE(LEFT(t.[text], 255), CHAR(10), ''), CHAR(13), '') AS [Short Query Text]
 ,qs.total_logical_reads AS [Total Logical Reads]
 ,qs.min_logical_reads AS [Min Logical Reads]
 ,qs.total_logical_reads / qs.execution_count AS [Avg Logical Reads]
 ,qs.max_logical_reads AS [Max Logical Reads]
 ,qs.min_worker_time AS [Min Worker Time]
 ,qs.total_worker_time / qs.execution_count AS [Avg Worker Time]
 ,qs.max_worker_time AS [Max Worker Time]
 ,qs.min_elapsed_time AS [Min Elapsed Time]
 ,qs.total_elapsed_time / qs.execution_count AS [Avg Elapsed Time]
 ,qs.max_elapsed_time AS [Max Elapsed Time]
 ,qs.execution_count AS [Execution Count]
 ,CASE WHEN CONVERT(NVARCHAR(max), qp.query_plan) COLLATE Latin1_General_BIN2 LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index]
 ,qs.creation_time AS [Creation Time]
--,t.[text] AS [Complete Query Text], qp.query_plan AS [Query Plan] -- uncomment out these columns if not copying results to Excel
FROM sys.dm_exec_query_stats AS qs WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS t
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
ORDER BY qs.total_logical_reads DESC
OPTION (RECOMPILE);

This script leverages two powerful Dynamic Management Views (DMVs) in SQL Server:

  • sys.dm_exec_query_plan: This DMV retrieves the query plan for a given plan handle, allowing you to see if any missing indexes are suggested.
  • sys.dm_exec_query_stats: This DMV provides aggregated performance statistics for cached query plans, including logical reads, worker time, and elapsed time.

By using these DMVs together, you get a comprehensive view of your most demanding queries, helping you make informed decisions about indexing and query tuning. Give it a try and see what insights you can uncover!

Saturday, March 28, 2020

How to and where to use inbuilt objects in SQL server

 

Exploring SQL Server Tools for Table, Index, and Constraint Information

Managing SQL Server databases often involves examining table structures, indexes, and usage patterns. SQL Server provides several built-in system stored procedures and views to help DBAs and developers gather this information efficiently. In this article, we’ll walk through useful tools and sample queries for inspecting database objects and their properties.


1. Retrieve Metadata with Built-in Stored Procedures

🔍 sp_help

The sp_help stored procedure returns details about a specific database object such as tables, views, or user-defined data types. It pulls metadata from the sys.sysobjects view.

📘 Documentation:
sp_help (Transact-SQL)


🔍 sp_helpindex

Use this procedure to get information about indexes defined on a table or view, including index name, columns involved, and index type.

📘 Documentation:
sp_helpindex (Transact-SQL)


🔍 sp_helpconstraint

This procedure lists all constraints (PRIMARY KEY, FOREIGN KEY, CHECK, DEFAULT, etc.) on a table, including the constraint name and related columns.

📘 Documentation:
sp_helpconstraint (Transact-SQL)


🔍 sp_spaceused

sp_spaceused provides size-related statistics for a table or the entire database, including reserved and used disk space and row counts.


2. Identify Recently Accessed Tables

You can track when a table was last accessed by users through different query operations (seek, scan, lookup, update). This can help identify stale or unused tables.

SELECT [schema_name], 
       table_name,
       MAX(last_access) AS last_access
FROM (
    SELECT 
        schema_name(schema_id) AS schema_name,
        name AS table_name,
        (
            SELECT MAX(last_access)
            FROM (
                VALUES (last_user_seek), 
                       (last_user_scan), 
                       (last_user_lookup), 
                       (last_user_update)
            ) AS tmp(last_access)
        ) AS last_access
    FROM sys.dm_db_index_usage_stats AS sta
    JOIN sys.objects AS obj 
        ON obj.object_id = sta.object_id 
        AND obj.type = 'U' 
        AND sta.database_id = DB_ID()
) AS usage
GROUP BY schema_name, table_name
ORDER BY last_access DESC;

This query can be customized or reused with minor changes depending on your needs.


3. Fetch Index Details

There are multiple ways to retrieve detailed index information:

✅ Index Columns with Table and Schema

SELECT s.name AS SchemaName,
       t.name AS TableName,
       i.name AS IndexName,
       c.name AS ColumnName
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.indexes i ON i.object_id = t.object_id
JOIN sys.index_columns ic ON ic.object_id = t.object_id
JOIN sys.columns c ON c.object_id = t.object_id AND ic.column_id = c.column_id
WHERE i.index_id > 0
  AND i.type IN (1, 2) -- Clustered & Non-clustered
  AND i.is_primary_key = 0
  AND i.is_unique_constraint = 0
  AND i.is_disabled = 0
  AND i.is_hypothetical = 0
  AND ic.key_ordinal > 0
ORDER BY ic.key_ordinal;

✅ Alternate Index Metadata View

SELECT t.name AS TableName,
       ind.name AS IndexName,
       ind.index_id AS IndexId,
       ic.index_column_id AS ColumnId,
       col.name AS ColumnName
FROM sys.indexes ind
JOIN sys.index_columns ic ON ind.object_id = ic.object_id AND ind.index_id = ic.index_id
JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id
JOIN sys.tables t ON ind.object_id = t.object_id
WHERE ind.is_primary_key = 0
  AND ind.is_unique = 0
  AND ind.is_unique_constraint = 0
  AND t.is_ms_shipped = 0
ORDER BY t.name, ind.name, ind.index_id, ic.index_column_id;

4. Find Tables without a Primary Key

Identifying tables without a primary key is critical for enforcing data integrity and improving performance.

✅ Option 1: Using sys.indexes

SELECT schema_name(tab.schema_id) AS schema_name,
       tab.name AS table_name
FROM sys.tables tab
LEFT JOIN sys.indexes pk 
    ON tab.object_id = pk.object_id AND pk.is_primary_key = 1
WHERE pk.object_id IS NULL
ORDER BY schema_name(tab.schema_id), tab.name;

✅ Option 2: Using OBJECTPROPERTY

SELECT SCHEMA_NAME(schema_id) AS SchemaName,
       name AS TableName
FROM sys.tables
WHERE OBJECTPROPERTY(OBJECT_ID, 'TableHasPrimaryKey') = 0
ORDER BY SchemaName, TableName;

Final Thoughts

Understanding and analyzing database objects is essential for efficient database administration. SQL Server offers powerful system views and stored procedures that reveal detailed metadata about tables, indexes, constraints, and usage. The examples above are great starting points for building your own diagnostic queries.

Have you used these tools in your environment? Share your experience or any other helpful queries you use!

How to get all database-level information using T-SQL

 

How to Retrieve Comprehensive Database Information Using System Views in SQL Server

When managing SQL Server instances, it’s often necessary to gather detailed information about each database—such as file sizes, recovery models, access settings, and last backup dates. This article demonstrates how to retrieve this information using system catalog views like sys.databases and sys.master_files.


What Information Can You Extract?

The query provided below will return a wide range of useful metadata for each database, including:

  • Database ID & Name
  • State (e.g., Online, Restoring, Recovering)
  • Number and Size of Data and Log Files
  • User Access Mode (Single-user, Multi-user)
  • Recovery Model (Simple, Full, Bulk Logged)
  • Compatibility Level (SQL Server version support)
  • Creation Date
  • Last Backup Status and Time
  • Various Options: Full-Text Enabled, Auto-Close, Auto-Shrink, Page Verify, Read-Only, etc.

SQL Script to Retrieve Database Metadata

SELECT
    database_id,
    CONVERT(VARCHAR(25), DB.name) AS dbName,
    state_desc,
    
    -- Count and Size of Data Files
    (SELECT COUNT(*) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'ROWS') AS DataFiles,
    (SELECT SUM((size * 8)/1024) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'ROWS') AS [Data MB],

    -- Count and Size of Log Files
    (SELECT COUNT(*) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'LOG') AS LogFiles,
    (SELECT SUM((size * 8)/1024) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'LOG') AS [Log MB],
    
    user_access_desc AS [User Access],
    recovery_model_desc AS [Recovery Model],

    -- Compatibility Level
    CASE compatibility_level
        WHEN 60 THEN '60 - SQL Server 6.0'
        WHEN 65 THEN '65 - SQL Server 6.5'
        WHEN 70 THEN '70 - SQL Server 7.0'
        WHEN 80 THEN '80 - SQL Server 2000'
        WHEN 90 THEN '90 - SQL Server 2005'
        WHEN 100 THEN '100 - SQL Server 2008 & R2'
        WHEN 110 THEN '110 - SQL Server 2012'
        WHEN 120 THEN '120 - SQL Server 2014'
        WHEN 130 THEN '130 - SQL Server 2016'
        WHEN 140 THEN '140 - SQL Server 2017'
        WHEN 150 THEN '150 - SQL Server 2019'
    END AS [Compatibility Level],

    -- Creation Date
    CONVERT(VARCHAR(20), create_date, 103) + ' ' + CONVERT(VARCHAR(20), create_date, 108) AS [Creation Date],

    -- Last Backup Information
    ISNULL((
        SELECT TOP 1 
            CASE TYPE
                WHEN 'D' THEN 'Full'
                WHEN 'I' THEN 'Differential'
                WHEN 'L' THEN 'Transaction Log'
            END + ' – ' + 
            LTRIM(ISNULL(STR(ABS(DATEDIFF(DAY, GETDATE(), backup_finish_date))) + ' days ago', 'NEVER')) + ' – ' + 
            CONVERT(VARCHAR(20), backup_start_date, 103) + ' ' + CONVERT(VARCHAR(20), backup_start_date, 108) + ' – ' + 
            CONVERT(VARCHAR(20), backup_finish_date, 103) + ' ' + CONVERT(VARCHAR(20), backup_finish_date, 108) + ' (' + 
            CAST(DATEDIFF(SECOND, backup_start_date, backup_finish_date) AS VARCHAR(4)) + ' seconds)'
        FROM msdb..backupset BK
        WHERE BK.database_name = DB.name
        ORDER BY backup_set_id DESC
    ), '-') AS [Last Backup],

    -- Optional Features and Settings
    CASE WHEN is_fulltext_enabled = 1 THEN 'Fulltext enabled' ELSE '' END AS [Fulltext],
    CASE WHEN is_auto_close_on = 1 THEN 'Auto-close enabled' ELSE '' END AS [AutoClose],
    page_verify_option_desc AS [Page Verify Option],
    CASE WHEN is_read_only = 1 THEN 'Read-only' ELSE '' END AS [Read Only],
    CASE WHEN is_auto_shrink_on = 1 THEN 'Auto-shrink enabled' ELSE '' END AS [AutoShrink],
    CASE WHEN is_auto_create_stats_on = 1 THEN 'Auto Create Statistics' ELSE '' END AS [Auto Create Stats],
    CASE WHEN is_auto_update_stats_on = 1 THEN 'Auto Update Statistics' ELSE '' END AS [Auto Update Stats],
    CASE WHEN is_in_standby = 1 THEN 'Standby Mode' ELSE '' END AS [Standby],
    CASE WHEN is_cleanly_shutdown = 1 THEN 'Cleanly Shutdown' ELSE '' END AS [Cleanly Shutdown]

FROM sys.databases DB
ORDER BY dbName, [Last Backup] DESC, name;

Reference

For more details, refer to the official Microsoft documentation:
📖 sys.databases (Transact-SQL)


Summary

This query is extremely useful for DBAs who want a consolidated snapshot of each database's configuration, storage usage, and maintenance status. By leveraging system views like sys.databases, sys.master_files, and msdb..backupset, you can automate health checks and create custom monitoring reports tailored to your environment.

 

How to find SQL Server Job history

 

How to View SQL Server Agent Job History

When troubleshooting or monitoring scheduled tasks, it's important to be able to review the execution history of SQL Server Agent jobs. This article outlines multiple methods to view job history using SQL Server Management Studio (SSMS), Transact-SQL (T-SQL), and system views in the msdb database.

QL Server Agent Job History

Method 1: Using SQL Server Management Studio (SSMS)

You can access job history directly from the SQL Server Management Studio interface:

  1. Open Object Explorer and connect to your SQL Server instance.
  2. Expand the SQL Server Agent node.
  3. Expand Jobs.
  4. Right-click the job you want to investigate and select View History.
  5. The Log File Viewer will display execution details.
    Use Refresh to reload the history or click Filter to narrow down the results.

Method 2: Using Transact-SQL

For quick access via T-SQL, run the following:

USE msdb;
GO

EXEC dbo.sp_help_jobhistory @job_name = N'';
GO

Replace <Job Name> with the name of your SQL Server Agent job to retrieve its execution history.


Method 3: Detailed T-SQL Script for Job History

To get a more comprehensive view, including step-level details and execution outcomes, use the query below:

SELECT 
    j.name AS JobName,
    j.date_created,
    j.description,
    s.step_id AS Step,
    s.step_name AS StepName,
    s.last_run_date,
    s.last_run_duration,
    s.database_name,
    msdb.dbo.agent_datetime(h.run_date, h.run_time) AS RunDateTime,
    ((run_duration / 10000 * 3600 + (run_duration / 100) % 100 * 60 + run_duration % 100 + 31) / 60) AS RunDurationMinutes,
    CASE h.run_status
        WHEN 0 THEN 'Failed'
        WHEN 1 THEN 'Succeeded'
        WHEN 2 THEN 'Retry'
        WHEN 3 THEN 'Canceled'
        WHEN 4 THEN 'In Progress'
    END AS RunStatus
FROM msdb.dbo.sysjobs j
INNER JOIN msdb.dbo.sysjobsteps s ON j.job_id = s.job_id
INNER JOIN msdb.dbo.sysjobhistory h ON s.job_id = h.job_id AND s.step_id = h.step_id AND h.step_id <> 0
WHERE j.enabled = 1
  AND h.run_date > 20200101
ORDER BY JobName, RunDateTime DESC;

💡 Note: You can uncomment the filters for job name or date range as needed.


This approach offers flexibility whether you prefer a visual interface or a script-based method. Monitoring job history is essential for diagnosing failures, optimizing performance, and ensuring scheduled processes complete as intended.

How to kick out all user from SQL server database

 

How to Safely Disconnect All Users from a SQL Server Database

There are situations when you need exclusive access to a SQL Server database—such as during maintenance, migration, or backup tasks. To achieve this, you may need to disconnect all existing users. Fortunately, SQL Server provides a safe and effective way to do this across most versions and editions.

Below is a simple method to switch a database to single-user mode, which immediately disconnects all users and rolls back any existing transactions.


Step 1: Set the Database to SINGLE_USER Mode

This command forces the database into single-user mode, rolling back any active connections immediately.

ALTER DATABASE <DatabaseName> SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO

USE <DatabaseName>;
GO

Once executed, only a single user (typically your current session) can connect to the database. This allows you to safely perform your administrative tasks without interference.


Step 2: Perform Your Maintenance Task

Carry out any required operations such as schema updates, data fixes, or backup procedures while the database is in single-user mode.


Step 3: Return the Database to MULTI_USER Mode

After completing your tasks, restore normal access by switching the database back to multi-user mode:

ALTER DATABASE <DatabaseName> SET MULTI_USER;
GO

Note on Rollbacks

In some cases, you may need to wait a few moments for SQL Server to finish rolling back active transactions after setting the database to single-user mode. Be patient and allow SQL Server to complete this process to avoid data corruption or partial changes.


This approach is reliable and recommended when you need full control over a SQL Server database, especially during critical operations. Always remember to revert to MULTI_USER mode to restore access for other users once your work is done.

Thursday, March 12, 2020

T-SQL function

T-SQL Function Overview in SQL Server

What Functions Are Available in SQL Server?

Microsoft SQL Server provides a rich set of built-in functions to help developers and administrators handle complex queries, data transformations, calculations, and data analysis. This article outlines the major categories of T-SQL functions, complete with helpful links to official documentation.


Analytic Functions

Analytic functions compute an aggregate value across a group of rows and return a value for each row. These are ideal for tasks such as running totals, moving averages, and ranking.


Aggregate Functions

These functions perform a calculation on a set of values and return a single value.


Configuration Scalar Functions

These functions return information about the current configuration of the SQL Server session.


Data Type Casting and Conversion Functions

Functions for converting and casting between SQL Server data types:


Security Functions

Symmetric Encryption & Decryption

Asymmetric Encryption & Decryption

Signature and Validation


Date and Time Data Types

SQL Server provides rich support for various date and time types. Each type has specific attributes including format, range, precision, and size.

  • time: High-precision time values

Date and Time Functions

Functions That Return Date Parts

Sunday, March 8, 2020

Top Procedure by execution count

Top Procedure by execution count

Ever wondered which stored procedures are hogging resources or running most frequently in your SQL Server database? This handy SQL query can help you pinpoint those performance bottlenecks by leveraging dynamic management views (DMVs) like `sys.dm_exec_procedure_stats` and `sys.dm_exec_query_plan`.

Unveiling Stored Procedure Performance

This script gives you a quick overview of your top 100 stored procedures based on their execution count. It's a great way to identify frequently used procedures that might benefit from optimization.

SELECT TOP (100) p.name AS [SP Name]
 ,qs.execution_count AS [Execution Count]
 ,ISNULL(qs.execution_count / DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute]
 ,qs.total_elapsed_time / qs.execution_count AS [Avg Elapsed Time]
 ,qs.total_worker_time / qs.execution_count AS [Avg Worker Time]
 ,qs.total_logical_reads / qs.execution_count AS [Avg Logical Reads]
 ,CASE WHEN CONVERT(NVARCHAR(max), qp.query_plan) COLLATE Latin1_General_BIN2 LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index]
 ,FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time]
 ,FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
-- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK) ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
 AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY qs.execution_count DESC
OPTION (RECOMPILE);

What the Query Reveals:

  • **SP Name:** The name of your stored procedure.
  • **Execution Count:** How many times the procedure has been executed since it was cached.
  • **Calls/Minute:** The execution rate – a good indicator of how busy a procedure is.
  • **Avg Elapsed Time:** The average total time taken for the procedure to complete.
  • **Avg Worker Time:** The average CPU time consumed by the procedure.
  • **Avg Logical Reads:** The average number of logical reads performed, indicating I/O activity.
  • **Has Missing Index:** A crucial flag that tells you if the query plan suggests a missing index, which could significantly boost performance if addressed.
  • **Last Execution Time:** When the procedure was last run.
  • **Plan Cached Time:** When the execution plan for this procedure was cached.

This script is built upon two powerful SQL Server DMVs:

By understanding these metrics, you can make informed decisions about where to focus your performance tuning efforts!

Saturday, March 7, 2020

Top Procedures By Avg Elapsed Time

 Top  Procedures By Avg Elapsed Time

Ever wondered which stored procedures are hogging your SQL Server's resources? Identifying slow-performing stored procedures is crucial for database optimization. This handy SQL query helps you pinpoint those resource-intensive procedures by analyzing their execution statistics.

Understanding Slow Stored Procedures with SQL

This query provides a comprehensive look at your stored procedures' performance, ordered by average elapsed time. It's a great starting point for identifying bottlenecks and understanding where you might need to focus your optimization efforts.

SELECT TOP (25) p.name AS [SP Name]
    ,qs.min_elapsed_time
    ,qs.total_elapsed_time / qs.execution_count AS [avg_elapsed_time]
    ,qs.max_elapsed_time
    ,qs.last_elapsed_time
    ,qs.total_elapsed_time
    ,qs.execution_count
    ,ISNULL(qs.execution_count / DATEDIFF(Minute, qs.cached_time, GETDATE()), 0) AS [Calls/Minute]
    ,qs.total_worker_time / qs.execution_count AS [AvgWorkerTime]
    ,qs.total_worker_time AS [TotalWorkerTime]
    ,CASE WHEN CONVERT(NVARCHAR(max), qp.query_plan) COLLATE Latin1_General_BIN2 LIKE N'%<MissingIndexes>%' THEN 1 ELSE 0 END AS [Has Missing Index]
    ,FORMAT(qs.last_execution_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Last Execution Time]
    ,FORMAT(qs.cached_time, 'yyyy-MM-dd HH:mm:ss', 'en-US') AS [Plan Cached Time]
    -- ,qp.query_plan AS [Query Plan] -- Uncomment if you want the Query Plan
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK) ON p.[object_id] = qs.[object_id]
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qs.database_id = DB_ID()
    AND DATEDIFF(Minute, qs.cached_time, GETDATE()) > 0
ORDER BY avg_elapsed_time DESC
OPTION (RECOMPILE);

How This Query Helps You:

  • SP Name: Clearly identifies the stored procedure.
  • avg_elapsed_time: This is key! It shows the average time taken for each execution, helping you spot the slowest procedures.
  • execution_count: Indicates how frequently the procedure runs. A high count with a high average elapsed time is a red flag.
  • Calls/Minute: Gives you an idea of the procedure's invocation rate.
  • Has Missing Index: A quick indicator if the procedure's query plan suggests missing indexes, which can significantly impact performance.

By running this query, you can get a snapshot of your database's procedure performance, enabling you to proactively address potential performance issues. Remember to run this in the context of the database you want to analyze.

Tuesday, March 3, 2020

Allways on Availability Group

 

Monitoring Always On Availability Groups in SQL Server

In this post, I’ll explain some commonly used internal SQL Server objects and queries for monitoring the health and performance of Always-On Availability Groups.

Always On Health and Status Overview

The following query gives a detailed overview of the health and synchronization status of availability groups:


SELECT ag.name AS [AG Name],
       ar.replica_server_name,
       ar.availability_mode_desc,
       adc.database_name,
       drs.is_local,
       drs.is_primary_replica,
       drs.synchronization_state_desc,
       drs.is_commit_participant,
       drs.synchronization_health_desc,
       drs.recovery_lsn,
       drs.truncation_lsn,
       drs.last_sent_lsn,
       drs.last_sent_time,
       drs.last_received_lsn,
       drs.last_received_time,
       drs.last_hardened_lsn,
       drs.last_hardened_time,
       drs.last_redone_lsn,
       drs.last_redone_time,
       drs.log_send_queue_size,
       drs.log_send_rate,
       drs.redo_queue_size,
       drs.redo_rate,
       drs.filestream_send_rate,
       drs.end_of_log_lsn,
       drs.last_commit_lsn,
       drs.last_commit_time,
       drs.database_state_desc
FROM sys.dm_hadr_database_replica_states AS drs WITH (NOLOCK)
INNER JOIN sys.availability_databases_cluster AS adc WITH (NOLOCK)
  ON drs.group_id = adc.group_id AND drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag WITH (NOLOCK)
  ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar WITH (NOLOCK)
  ON drs.group_id = ar.group_id AND drs.replica_id = ar.replica_id
ORDER BY ag.name, ar.replica_server_name, adc.database_name
OPTION (RECOMPILE);

Show Availability Groups Where This Server Is Secondary


SELECT Groups.name AS AGname,
       primary_replica
FROM sys.dm_hadr_availability_group_states States
INNER JOIN master.sys.availability_groups Groups
  ON States.group_id = Groups.group_id
WHERE primary_replica != @SERVERNAME;

Show Databases in AG Where This Server Is Primary


SELECT Groups.name AS AGname,
       AGD.database_name
FROM sys.dm_hadr_availability_group_states States
INNER JOIN master.sys.availability_groups Groups
  ON States.group_id = Groups.group_id
INNER JOIN sys.availability_databases_cluster AGD
  ON Groups.group_id = AGD.group_id
WHERE primary_replica = @SERVERNAME
ORDER BY AGname, database_name;

Replica and Availability Group Status


WITH AGStatus AS (
  SELECT name AS AGname,
         replica_server_name,
         CASE WHEN (primary_replica = replica_server_name) THEN 1 ELSE 0 END AS IsPrimaryServer,
         secondary_role_allow_connections_desc AS ReadableSecondary,
         availability_mode AS Synchronous,
         failover_mode_desc
  FROM master.sys.availability_groups Groups
  INNER JOIN master.sys.availability_replicas Replicas
    ON Groups.group_id = Replicas.group_id
  INNER JOIN master.sys.dm_hadr_availability_group_states States
    ON Groups.group_id = States.group_id
)
SELECT AGname,
       Replica_server_name,
       IsPrimaryServer,
       Synchronous,
       ReadableSecondary,
       Failover_mode_desc
FROM AGStatus
ORDER BY AGname, IsPrimaryServer DESC;

All AG Availability Group Status


SELECT AG.name AS AvailabilityGroupName,
       AR.replica_server_name AS AvailabilityReplicaServerName,
       dbcs.database_name AS AvailabilityDatabaseName,
       ISNULL(dbcs.is_failover_ready, 0) AS IsFailoverReady,
       ISNULL(arstates.role_desc, 3) AS ReplicaRole,
       AR.availability_mode_desc AS AvailabilityMode
ORDER BY AvailabilityReplicaServerName, AvailabilityDatabaseName;

(Note: Long query shortened for brevity—see full script for details.)

Performance and Estimation Queries


SELECT AG.name AS AvailabilityGroupName,
       AR.replica_server_name AS AvailabilityReplicaServerName,
       dbcs.database_name AS AvailabilityDatabaseName
ORDER BY AvailabilityDatabaseName;

AG Log Reads


EXEC sp_readerrorlog 0, 1, 'The state of the local availability replica', '';
EXEC sp_readerrorlog 0, 1, 'RESOLVING';

Internal SQL Server Objects for Always On


SELECT * FROM sys.dm_hadr_cluster_members;
SELECT * FROM sys.dm_hadr_cluster;
SELECT * FROM sys.dm_hadr_cluster_networks;
SELECT * FROM sys.dm_hadr_auto_page_repair;
...
SELECT * FROM sys.availability_groups;

SQL Server Extended Events for Always On


SELECT xml_data.value('(event/@name)[1]', 'varchar(max)') AS Name,
       ...
FROM (
  SELECT object_name, CONVERT(xml, event_data) AS xml_data
  FROM sys.fn_xe_file_target_read_file('AlwaysOn*.xel', NULL, NULL, NULL)
) AS XEventData
ORDER BY Time;

Stored Procedure: Calculate RPO


CREATE PROCEDURE uspCalculateRPO (
  @group_id UNIQUEIDENTIFIER,
  @replica_id UNIQUEIDENTIFIER,
  @group_database_id UNIQUEIDENTIFIER
)
AS
BEGIN
  ...
END

Stored Procedure: Calculate RTO


CREATE PROCEDURE uspCalculateRTO (@secondary_database_name NVARCHAR(MAX))
AS
BEGIN
  ...
END

Sunday, March 1, 2020

How to find similar index in SQL Server Query

This SQL query helps identify duplicate indexes based on a specific column name. It retrieves details about indexes that are similar to each other in terms of the column they index.

SQL Query to Find Duplicate Indexes by Column Name

The following query can be used to find duplicate indexes in your database. The duplicates are determined by comparing the columns used in different indexes within the same table.

-- Query to identify similar indexes

SELECT 
  s.Name + N'.' + t.name AS [Table],          -- Table name
  i1.index_id AS [Index1 ID],                  -- First index ID
  i1.name AS [Index1 Name],                    -- First index name
  dupIdx.index_id AS [Index2 ID],              -- Duplicate index ID
  dupIdx.name AS [Index2 Name],                -- Duplicate index name
  c.name AS [Column]                           -- Column name

FROM sys.tables t
JOIN sys.indexes i1 ON t.object_id = i1.object_id
JOIN sys.index_columns ic1 ON ic1.object_id = i1.object_id
  AND ic1.index_id = i1.index_id
  AND ic1.index_column_id = 1
JOIN sys.columns c ON c.object_id = ic1.object_id
  AND c.column_id = ic1.column_id
JOIN sys.schemas s ON t.schema_id = s.schema_id

CROSS APPLY (
  SELECT 
    i2.index_id,                              -- Duplicate index ID
    i2.name                                   -- Duplicate index name
  FROM sys.indexes i2
  JOIN sys.index_columns ic2 ON ic2.object_id = i2.object_id
    AND ic2.index_id = i2.index_id
    AND ic2.index_column_id = 1
  WHERE i2.object_id = i1.object_id
    AND i2.index_id > i1.index_id
    AND ic2.column_id = ic1.column_id
) dupIdx

ORDER BY s.name, t.name, i1.index_id;          -- Sorting by schema, table, and index ID

Make sure to customize this query based on your database structure and indexing strategy for optimal results.

Popular Posts