I have a seemingly simple request. I need to script all the stored procedures in a database following this criteria.
The scripts need to include dropping if the proc exists like follows...
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[myproc]') AND type in (N'P', N'PC'))
The script cannot have sp_executesql so nothing like this...
EXEC dbo.sp_executesql @statement = '....'
I need a separate script file for each stored procedure. So it would be [stored procedure name].sql
I notice when I try the built in sql generate scripts I can get the procs in a separate file via the checkbox for scripting objects in separate files and I can also get the if exists drop. However, it uses sp_executesql which they don't want.
So I spent a bit trying SMO and found similar issues...
A. Sadly the following just scripts only the drop statements. No way I see to combine it with the create. So I can get the separate files and no sp_executesql but I'm still missing #1 above
Scripter scripter = new Scripter();
scripter.Options.ScriptDrops = true;
B. Secondly, the following option changes the output to use sp_executesql
scripter.Options.IncludeIfNotExists = true;
C. Finally, I can add the text manually. It successfully is set in the TextHeader. However, the scripter.Script() throws an exception "{"Script failed for StoredProcedure 'dbo.myproc'. "}
storedProcedure.TextHeader = string.Format("IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'{0}') AND type in (N'P', N'PC')) \r\nDROP PROCEDURE {0} \r\nGO\r\n{1}", storedProcedure.Name, storedProcedure.TextHeader);
scripter.Options.FileName = Path.Combine(storedProceduresPath, storedProcedure.Name + ".sql");
scripter.Script(new Urn[] { storedProcedure.Urn }); //Exception! - Script failed for StoredProcedure
I can't imagine this is all that strange of a thing to do so I'm wondering how people are accomplishing this??? Sad if needing to create the separate files using the sql - tasks - generate scripts, followed by an app to clear out the unwanted "EXEC dbo.sp_executesql @statement = N'"
The following code samples are in PowerShell, but uses SMO, so you can easily transform it to c#.
Scripter felt a bit slow, so i'm using a slightly unorthodox method, but this worked for hundreds of times for me.
$sp
is a stored procedure in your db, just foreaching all SPs:
foreach($sp in $db.StoredProcedures)
$sbHead.AppendLine( [string]::Format(@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[{0}].[{1}]') AND type in (N'P', N'PC'))
GO
"@, $sp.Schema, $sp.Name))
$sbBody.AppendLine( [string]::Format(@"
print 'creating "{1}"...'
-- * * * * #{0}:{1}; CreateDate:{2}, DateLastModified:{3} * * * *
GO
{4}
{5}
GO
"@, $cnt, $sp.Name, $sp.CreateDate, $sp.DateLastModified, $sp.TextHeader, $sp.TextBody))
This prints out some extra info:
print 'creating "{1}"...'
put it in there because one time the
creation just hung for minutes. Knowing the current SP name lead us
to the source of the problem: connection to a linked server was
down... This method does not takes script dependencies into account, so you will get a warning message in the console like "The current SP xy depends SP dsa, but creating it anyway". But runs faster because of not working out those dependencies. Worked OF for me all the time...
Uploaded the full script, and an example output to GitHub: SqlScriptExport.ps1
This one does a few extra things: