Search This Blog

Monday, July 10, 2023

A Barebone Script To Generate SQL Script to Create Synonyms

A Barebone Script To Generate SQL Script to Create Synonyms

This stored procedure can be used to generate a SQL script to create synonyms for all tables in a source database. Optionally, on the local SQL instance this SP can also create the synonyms for you, provided the target database and the schema (defaults to dbo and therefore can be omitted) already exists there. 

-- exec spGenerateSynonyms @sourcedb = 'AdminDBA', @targetdb = 'AdminDBA', @sourceschema= 'dbo', @targetschema='newschema', @printOnly = 1
/*
Description: 
Generates script to create synonyms in target db (usually, a blank db) for 
all tables that exist in source db (usually a subscriber db)

Requirements:
Source database and source schema (defaults to dbo so it can be omitted) 
must exist If parameter @PrintOnly is set to 0, meaning the script would  
also run the create synonyms statements then the  target database and 
schema must exist too... 

Caveats:
For now this procedure only generates scripts for tables. 
Synonyms on other types of objects (views, stored procedures etc.) are 
also supported by sql server.


Examples:
exec spGenerateSynonyms @sourcedb = 'EnterpriseSub', @targetdb = 'Enterprise', @printOnly = 1
exec spGenerateSynonyms @sourcedb = 'AdminDBA', @targetdb = 'Enterprise', @sourceschema='dbo',  @targetschema='myapp',  @printOnly = 1

exec spGenerateSynonyms @sourcedb = 'AdminDBA', @targetdb = 'Enterprise', @sourceSchema = 'appuser1', @printOnly = 1
exec spGenerateSynonyms @sourcedb = 'AdminDBA', @targetdb = 'AdminDBA', @targetschema='newschema', @printOnly = 0

exec spGenerateSynonyms @targetdb = 'ProductUserMaster' -- INVALID
exec spGenerateSynonyms @sourcedb = 'AdminDBA', @targetdb = 'AdminDBA'  -- INVALID, SOURCE AND TARGET  DATABASES + SCHEMAS CANNOT BE SAME

*/


set nocount on
go
use AdminDBA   -- Change this to the whatever database you are using to store DBA objects
go
if object_id('spGenerateSynonyms') is null
BEGIN
   PRINT 'Creating stored procedure spGenerateSynonyms'
   exec('create PROCEDURE spGenerateSynonyms as select '''' empty_string')
END
ELSE
   PRINT 'Stored procedure spGenerateSynonyms already exists.'
   PRINT 'Modifying stored procedure spGenerateSynonyms....'
GO
alter  PROCEDURE spGenerateSynonyms 
(
   @sourceDB sysname,                 -- aka base database where the objecgs are located
   @targetDB sysname,                 -- database where the synonyms will be created

   @object_type varchar(100) = 'Tables',   -- type of objects to create synonyms

   @sourceSchema sysname = 'dbo',     -- base schema name... defaults to dbo
   @targetSchema sysname = 'dbo',     -- defaults to dbo

   @PrintOnly bit = 1                 -- whether to also create the synonums or only print the SQL statements to create them
)


as
set nocount on

declare @sql varchar(4000), @def nvarchar(max)
declare @sql_string nvarchar(4000)

declare @sourceschema_id table(schema_id int)
declare @targetschema_id table(schema_id int)


select @sourcedb SourceDB, @sourceSchema SourceSchema, @targetdb TargetDB, @targetSchema TargetSchema

-- ************* VALIDATE INPUTS *********************
if @targetdb is null or @sourcedb is null
begin
   raiserror('Error: Source and target database names are required and cannot be null. Exiting...', 16, 1)
   return -1
end

if @targetdb  = @sourcedb and @sourceSchema = @targetSchema
begin
   raiserror('Error: Source and taget databases + schemas cannot be the same. Exiting...', 16, 1)
   return -1
end

if  db_id(@sourcedb) is null or  databasepropertyex(@sourcedb, 'Status')!='ONLINE'
begin
   raiserror('Error: Source database %s not found or not ONLINE. Exiting...', 16, 1, @sourcedb)
   return -1
end

-- If @PrintOnly=0 the SP will try to create the sysnonyms
-- Therefore the target db and the target schema must already exist
if @PrintOnly = 0 
BEGIN

		if  (db_id(@targetDB ) is null or  databasepropertyex(@targetDB , 'Status')!='ONLINE')
		begin
		   raiserror('Error: The target database %s not found or not ONLINE. Exiting...', 16, 1, @targetDB )
		   return -1
		end

		set @sql = 'select schema_id from [' + @targetDB + '].sys.schemas where name = ''' + @targetschema + ''''
		insert into @targetschema_id  exec (@sql)
		if not exists (select * from @targetschema_id)
		BEGIN
		   raiserror('Error: Target schema %s not found in the %s database . Exiting...', 16, 1, @targetSchema, @targetdb)
		   return -1
		END


END




set @sql = 'select schema_id from [' + @sourceDB + '].sys.schemas where name = ''' + @sourceSchema + ''''
insert into @sourceschema_id exec (@sql)
if not exists (select * from @sourceschema_id)
BEGIN
	raiserror('Error: Source schema %s not found in the %s database . Exiting...', 16, 1, @sourceschema, @sourceDB)
	return -1
END



if object_id('tempdb..#t1_synonyms_definitions') is not null
   drop table #t1_synonyms_definitions
create table #t1_synonyms_definitions(definition nvarchar(max))
set @sql = 'select  ''
Use [' + @Targetdb + ']
IF NOT EXISTS (SELECT * FROM sys.synonyms WHERE schema_id = schema_id(''''' + @targetSchema + ''''') and name = N'''''' + NAME + '''''') 
CREATE SYNONYM [' + @targetSchema + '].['' + name + ''] FOR [' + @sourcedb + '].[' + @sourceSchema + '].['' + name  + '']'' from [' + @sourcedb + '].sys.tables'

if @sourceSchema is not null
set @sql = @sql + ' where schema_name(schema_id) = ''' + @sourceSchema + ''''

insert into #t1_synonyms_definitions exec (@sql)
select * from #t1_synonyms_definitions
declare c1 cursor for select * from #t1_synonyms_definitions
open c1
fetch c1 into @def 
while @@fetch_status = 0
begin
      print @def


      if @printOnly = 0 exec (@def)
      fetch c1 into @def 

end
close c1
deallocate c1

But why synonyms? Synonyms can mask/hide the underlying location of the base objects (think of linked server queries, cross database queries etc.),  regardless of who owns them and/or their names.  Or maybe you have two identical copies of the same app/code, one pointing to the live data and the other for the archive/historical data.

You might be wondering, well views can achieve the same and I can add some logic to make them even useful. And you would be right, views are more useful and certainly has more use cases. But if you are creating views only to mask the underlying location of tables or their names, then I would say synonyms are better because for one thing, the views would not automatically reflect changes to the underlying tables structures. Of course then there are good use cases where you don't want a view to exactly reflect the underlying table structure.