How to run a script on all databases that are either AG primary or standalone in SQL instance

Recently I had a requirement to run a script on all the databases that are either AG primary or standalone in SQL instance. Although for a single database we can easily determine of that database belongs to rpimary replica or not using function “sys.fn_hadr_is_primary_replica” but challenge for me was to get a list of all DBs which were either primary or standalone on the particular replica instance.

When I searched through internet, I found below useful queries for my case which was giving me output as DBName, AGName and AGType

Query 1:

select

sd.name,

coalesce(grp.ag_name,'N/A'),

case

when reps.is_primary_replica = 1 then 'PRIMARY'

when reps.is_primary_replica = 0 then 'SECONDARY'

else 'NOT REPLICATED'

end as AGType

from sys.databases as sd

left outer join sys.dm_hadr_database_replica_states as reps

on reps.database_id = sd.database_id

and reps.replica_id = sd.replica_id

left outer join sys.dm_hadr_name_id_map as grp

on grp.ag_id = reps.group_id

Query 2:
 

select DISTINCT sd.name,

(

case

 when

  hdrs.is_primary_replica IS NULL then  'NOT REPLICATED'

 when exists ( select * from sys.dm_hadr_database_replica_states as irs where sd.database_id = irs.database_id and is_primary_replica = 1 ) then

'PRIMARY'

 else

    'SECONDARY'

 end

) as  AGType,

COALESCE(grp.ag_name,'N/A') as AGName

 from sys.databases as sd

 left outer join sys.dm_hadr_database_replica_states  as hdrs on hdrs.database_id = sd.database_id

 left outer join sys.dm_hadr_name_id_map as grp on grp.ag_id = hdrs.group_id

As I wanted to filter the output from above queries to get list of databases that are either AG primary or standalone in SQL instance, I used common table expression (CTE) to achieve it.

CTE of Query 1:

;WITH DBLIST as (select

sd.name,

coalesce(grp.ag_name,'N/A') as AGNAME,

case

when reps.is_primary_replica = 1 then 'PRIMARY'

when reps.is_primary_replica = 0 then 'SECONDARY'

else 'NOT REPLICATED'

end as AGType

from sys.databases as sd

left outer join sys.dm_hadr_database_replica_states as reps

on reps.database_id = sd.database_id

and reps.replica_id = sd.replica_id

left outer join sys.dm_hadr_name_id_map as grp

on grp.ag_id = reps.group_id

)

SELECT @@SERVERNAME as SERVERNAME,* from DBLIST where AGType <>'Secondary' and name NOT IN ('master', 'model', 'msdb', 'tempdb')

CTE of Query 2:
 

WITH DBLIST as (

select DISTINCT sd.name,

(

case

 when

  hdrs.is_primary_replica IS NULL then  'NOT REPLICATED'

 when exists ( select * from sys.dm_hadr_database_replica_states as irs where sd.database_id = irs.database_id and is_primary_replica = 1 ) then

'PRIMARY'

 else

    'SECONDARY'

 end

) as  AGType,

COALESCE(grp.ag_name,'N/A') as AGName

 from sys.databases as sd

 left outer join sys.dm_hadr_database_replica_states  as hdrs on hdrs.database_id = sd.database_id

 left outer join sys.dm_hadr_name_id_map as grp on grp.ag_id = hdrs.group_id)

 SELECT @@SERVERNAME as SERVERNAME,* from DBLIST where AGType <>'Secondary' and name NOT IN ('master', 'model', 'msdb', 'tempdb')

Once CTE expression was ready I used it to execute the desired query on all databases through cursor.

-------------------------------------------------------------------

-- Declare a cursor to loop through all the databases on the server

-------------------------------------------------------------------

DECLARE csrDB CURSOR FOR

WITH DBLIST as (

select DISTINCT sd.name,

(

case

 when

  hdrs.is_primary_replica IS NULL then  'NOT REPLICATED'

 when exists ( select * from sys.dm_hadr_database_replica_states as irs where sd.database_id = irs.database_id and is_primary_replica = 1 ) then

'PRIMARY'

 else

    'SECONDARY'

 end

) as  AGType,

COALESCE(grp.ag_name,'N/A') as AGName

 from sys.databases as sd

 left outer join sys.dm_hadr_database_replica_states  as hdrs on hdrs.database_id = sd.database_id

 left outer join sys.dm_hadr_name_id_map as grp on grp.ag_id = hdrs.group_id

 where sd.state_desc = 'ONLINE')

  SELECT name from DBLIST where AGType <>'Secondary'

        and  name NOT IN ('master', 'model', 'msdb', 'tempdb')

---------------------------------------------------

-- Open the cursor and get the first database name

---------------------------------------------------
 

OPEN csrDB

FETCH NEXT

    FROM csrDB

    INTO @DBName

-- Loop through the cursor

WHILE @@FETCH_STATUS = 0

    BEGIN

---------------------------------------

<Desired Code to run on all databases>

---------------------------------------
 

-- Get the next database name

        FETCH NEXT

            FROM csrDB

            INTO @DBName

-- End of the cursor loop

    END

-- Close and deallocate the CURSOR

CLOSE csrDB

DEALLOCATE csrDB

Hope you will find this helpful!

Happy learning!!

References:
https://dimitri.janczak.net/2016/07/08/database-part-availability-group/

https://learn.microsoft.com/en-us/sql/relational-databases/system-functions/sys-fn-hadr-is-primary-replica-transact-sql?view=sql-server-ver16&viewFallbackFrom=sql-server-ver16%2F