Introduction
So you have added a number of new tables to your database and you now need to create stored procedures to insert, update, and delete from them. Now, depending on how many tables you have added,this can be a very time consuming task but fear not, as I will show you an automated solution that will build the procedures for you.
The code below will work for any table with a single primary key that is also an identity column. It could be extended to handle all different forms of tables but, in the time it would take you to get this working perfectly, you could have written the extra procedures manually! I personally try to automate as much of the repetitive work as I can, leaving me more time to spend on the good stuff, and I can bet the majority of you would like to do this as well.
You will also note that no select or "get" procedures are produced. Again, this is because these inevitably require more than one table and so cannot be automated easily.
So, let's dive in. We'll see three functions being used below; createInsertSP, createUpdateSP, and createDeleteSP (not the most inventive names, I know, but they do what they say on the tin!).
How it all works
All three functions share a similar structure and it would be too long-winded to go through all three, so we'll just concentrate on one so you can see how this works. All three functions return both the drop and create T-SQL strings for the procedure in question, separated by a pair of pipe characters.
The Create Insert Procedure Function
The logic in the create insert procedure function is as follows:
- Creates a drop statement sql string for the procedure (in case you are re-running the process!)
- Creates the create statement sql string like so:
- Create the parameter list (excluding the identity primary key but including defaults)
- Create a commented modifications section
- Begin a transaction
- Begin a try
- Create the insert statement and the return of the new identity (primary key) value
- Commit the transaction
- End the try
- Begin a catch
- Raise any error
- Roll back the transaction
- End the catch
- Return both the drop and the create strings separated by two pipe characters
Ok, so let's have a look at the code.
CREATE FUNCTION dbo.createInsertSP ( @spSchema varchar(200), -- desired schema @spTable varchar(200) -- desired table ) RETURNS varchar(max) AS BEGIN declare @SQL_DROP varchar(max) declare @SQL varchar(max) declare @COLUMNS varchar(max) declare @PK_COLUMN varchar(200) set @SQL = '' set @SQL_DROP = '' set @COLUMNS = '' -- step 1: generate the drop statement and then the create statement set @SQL_DROP = @SQL_DROP + 'IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[' + @spSchema + '].[insert' + @spTable + ']'') AND type in (N''P'', N''PC''))' + char(13) set @SQL_DROP = @SQL_DROP + 'DROP PROCEDURE [' + @spSchema + '].[insert' + @spTable + ']' set @SQL = @SQL + 'CREATE PROC [' + @spSchema + '].[insert' + @spTable + ']' + char(13) set @SQL = @SQL + '(' + char(13) -- step 2: ascertain what the primary key column for the table is set @PK_COLUMN = ( select c.column_name from information_schema.table_constraints pk inner join information_schema.key_column_usage c on c.table_name = pk.table_name and c.constraint_name = pk.constraint_name where pk.TABLE_SCHEMA = @spSchema and pk.TABLE_NAME = @spTable and pk.constraint_type = 'primary key' and c.column_name in ( select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where columnproperty(object_id(quotename(@spSchema) + '.' + quotename(@spTable)), COLUMN_NAME, 'IsIdentity') = 1 -- ensure the primary key is an identity column group by COLUMN_NAME ) group by column_name having COUNT(column_name) = 1 -- ensure there is only one primary key ) -- step 3: now put all the table columns in bar the primary key (as this is an insert and it is an identity column) select @COLUMNS = @COLUMNS + '@' + COLUMN_NAME + ' as ' + (case DATA_TYPE when 'numeric' then DATA_TYPE + '(' + convert(varchar(10), NUMERIC_PRECISION) + ',' + convert(varchar(10), NUMERIC_SCALE) + ')' else DATA_TYPE end) + (case when CHARACTER_MAXIMUM_LENGTH is not null then '(' + case when CONVERT(varchar(10), CHARACTER_MAXIMUM_LENGTH) = '-1' then 'max' else CONVERT(varchar(10), CHARACTER_MAXIMUM_LENGTH) end + ')' else '' end) + (case when IS_NULLABLE = 'YES' then case when COLUMN_DEFAULT is null then ' = Null' else '' end else case when COLUMN_DEFAULT is null then '' else case when COLUMN_NAME = @PK_COLUMN then '' else ' = ' + replace(replace(COLUMN_DEFAULT, '(', ''), ')', '') end end end) + ',' + char(13) from INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = @spSchema and TABLE_NAME = @spTable and COLUMN_NAME <> @PK_COLUMN order by ORDINAL_POSITION set @SQL = @SQL + left(@COLUMNS, len(@COLUMNS) - 2) + char(13) set @SQL = @SQL + ')' + char(13) set @SQL = @SQL + 'AS' + char(13) set @SQL = @SQL + '' + char(13) -- step 4: add a modifications section set @SQL = @SQL + '-- Author: Auto' + char(13) set @SQL = @SQL + '-- Created: ' + convert(varchar(11), getdate(), 106) + char(13) set @SQL = @SQL + '-- Function: Inserts a ' + @spSchema + '.' + @spTable + ' table record' + char(13) set @SQL = @SQL + '' + char(13) set @SQL = @SQL + '-- Modifications:' + char(13) set @SQL = @SQL + '' + char(13) -- body here -- step 5: begins a transaction set @SQL = @SQL + 'begin transaction' + char(13) + char(13) -- step 6: begin a try set @SQL = @SQL + 'begin try' + char(13) + char(13) set @SQL = @SQL + '-- insert' + char(13) -- step 7: code the insert set @COLUMNS = '' select @COLUMNS = @COLUMNS + '@' + COLUMN_NAME + ',' from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @spTable and COLUMN_NAME <> @PK_COLUMN order by ORDINAL_POSITION set @COLUMNS = left(@COLUMNS, len(@COLUMNS) -1) -- trim off the last comma set @SQL = @SQL + 'insert [' + @spSchema + '].[' + @spTable + '] (' + replace(@COLUMNS, '@', '') + ')' + char(13) set @SQL = @SQL + 'values (' + @COLUMNS + ')' + char(13) set @SQL = @SQL + char(13) + char(13) set @SQL = @SQL + '-- Return the new ID' + char(13) set @SQL = @SQL + 'select SCOPE_IDENTITY();' + char(13) + char(13) -- step 8: commit the transaction set @SQL = @SQL + 'commit transaction' + char(13) + char(13) -- step 9: end the try set @SQL = @SQL + 'end try' + char(13) + char(13) -- step 10: begin a catch set @SQL = @SQL + 'begin catch' + char(13) + char(13) -- step 11: raise the error set @SQL = @SQL + ' declare @ErrorMessage NVARCHAR(4000);' + char(13) set @SQL = @SQL + ' declare @ErrorSeverity INT;' + char(13) set @SQL = @SQL + ' declare @ErrorState INT;' + char(13) + char(13) set @SQL = @SQL + ' select @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();' + char(13) + char(13) set @SQL = @SQL + ' raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);' + char(13) + char(13) set @SQL = @SQL + ' rollback transaction' + char(13) + char(13) -- step 11: end the catch set @SQL = @SQL + 'end catch;' + char(13) + char(13) -- step 12: return both the drop and create statements RETURN @SQL_DROP + '||' + @SQL END GO
As you can see, there is nothing hugely complicated here, just a couple of queries to the system tables to get the tables primary, columns, and defaults, then a large amount of string concatenation. Admittedly functions such as this are not fun to write but believe me, when you have used them over and over again, you'll be glad that you put the time in to start with.
The Create Update Procedure Function
This follows the same basic structure as the create insert function procedure except that it obviously codes in an update as opposed to an insert.
The Create Delete Procedure Function
This follows the same basic structure as the create insert function procedure except that it obviously codes in a delete as opposed to an insert and only takes one parameter which is the primary key.
Putting it all together
So now that we have three create functions - and hopefully a good idea of what they do - it's time to put them to work.
To do this I instantiate a memory table, fill it with the tables that I wish to create the stored procedures for, and then loop through executing the three functions to get the SQL to create the procedures which I then execute as dynamic SQL. In the below example I have coded the procedures to be created for every table in the database that has one identity primary key column but obviously you need to alter this to suit your needs.
Let's have a look at the code.
set nocount on; -- write the sps we require -- create the local variables declare @spId int declare @spSchema varchar(200) declare @spTable varchar(200) declare @spInsertSQL varchar(max) declare @spUpdateSQL varchar(max) declare @spDeleteSQL varchar(max) declare @dropPoint int declare @drop_sp_sql varchar(max) declare @create_sp_sql varchar(max) declare @spsToWrite table(spId int identity(1,1), spSchema varchar(200), spTable varchar(200)) -- populate the list of tables to process insert into @spsToWrite(spSchema, spTable) select ist.TABLE_SCHEMA, ist.TABLE_NAME from INFORMATION_SCHEMA.TABLES ist inner join ( select c.table_schema, c.table_name from information_schema.table_constraints pk inner join information_schema.key_column_usage c on c.table_name = pk.table_name and c.constraint_name = pk.constraint_name where pk.constraint_type = 'primary key' and c.column_name in ( select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where columnproperty(object_id(quotename(c.table_schema) + '.' + quotename(c.table_name)), COLUMN_NAME, 'IsIdentity') = 1 -- column is an identity column group by COLUMN_NAME ) group by c.table_schema, c.table_name having count(c.column_name) = 1 -- table only has one primary key ) tables_with_one_identity_pk on ist.table_schema = tables_with_one_identity_pk.table_schema and ist.table_name = tables_with_one_identity_pk.table_name where (ist.TABLE_TYPE = 'BASE TABLE' and ist.TABLE_NAME not like 'ddl%' and ist.TABLE_NAME not like 'sys%') -- add any further where clause restrictions here: certain schema, specific table names, etc. order by TABLE_NAME begin try -- get the first table to process select @spId = (select top 1 spId from @spsToWrite order by spId) -- loop through each table and create the desired stored procedures while (@spId <> 0) begin select @spSchema = spSchema, @spTable = spTable from @spsToWrite where spId = @spId set @drop_sp_sql = '' set @create_sp_sql = '' -- write an insert procedure for this table set @spInsertSQL = dbo.createInsertSP(@spSchema, @spTable) set @dropPoint = CHARINDEX('||', @spInsertSQL) set @drop_sp_sql = left(@spInsertSQL, @dropPoint - 1) set @create_sp_sql = right(@spInsertSQL, len(@spInsertSQL) - (@dropPoint + 1)) execute(@drop_sp_sql) -- drop any existing procedure execute(@create_sp_sql) -- create the new one --print @drop_sp_sql --print 'GO' --print @create_sp_sql --print 'GO' -- write an update procedure for this table set @spUpdateSQL = dbo.createUpdateSP(@spSchema, @spTable) set @dropPoint = CHARINDEX('||', @spUpdateSQL) set @drop_sp_sql = left(@spUpdateSQL, @dropPoint - 1) set @create_sp_sql = right(@spUpdateSQL, len(@spUpdateSQL) - (@dropPoint + 1)) execute(@drop_sp_sql) -- drop any existing procedure execute(@create_sp_sql) -- create the new one --print @drop_sp_sql --print 'GO' --print @create_sp_sql --print 'GO' -- write a delete sp for this table set @spDeleteSQL = dbo.createDeleteSP(@spSchema, @spTable) -- code in delete set @dropPoint = CHARINDEX('||', @spDeleteSQL) set @drop_sp_sql = left(@spDeleteSQL, @dropPoint - 1) set @create_sp_sql = right(@spDeleteSQL, len(@spDeleteSQL) - (@dropPoint + 1)) execute(@drop_sp_sql) -- drop any existing procedure execute(@create_sp_sql) -- create the new one --print @drop_sp_sql --print 'GO' --print @create_sp_sql --print 'GO' -- delete the table just processed from the working table delete from @spsToWrite where spId = @spId -- get the next table set @spId = 0 select @spId = (select top 1 spId from @spsToWrite order by spId) end end try begin catch select ERROR_NUMBER() AS ErrorNumber, ERROR_SEVERITY() AS ErrorSeverity, ERROR_STATE() AS ErrorState, ERROR_PROCEDURE() AS ErrorProcedure, ERROR_LINE() AS ErrorLine, ERROR_MESSAGE() AS ErrorMessage; end catch go
There are commented out print statements in the code, as you may want to just test this without creating any objects and if so, just comment out the execute lines and comment back in the print lines.
Conclusion
So there you have it, an automated solution to creating stored procedures for your tables. Obviously you will need to tweak it to fit your personal or corporate preferences but a little time invested will pay you back big time in the long run. All the necessary scripts are attached so please, give it a try.