I want to be able to automatically enable/disable a SQL Agent job by determining which server is the active one and which is the passive one. In other words, if the primary server where the job is enabled, fails over to the secondary server, then the job will be disabled and the job on the secondary server (now the primary) will be enabled.
I found a script that does exactly what I need and modified slightly to my standards:
http://sqlmag.com/blog/alwayson-availability-groups-and-sql-server-jobs-part-28-additional-options-tackling-jobs-failo
ALTER procedure [dbo].[SQLAgentJobFailover] (#agname varchar(200))
AS
BEGIN
declare #is_primary_replicate bit
declare #job_name VARCHAR(100) = 'MySQLAgentJobName'
declare #job_enabled bit
select #is_primary_replicate = master.dbo.fn_hadr_group_is_primary(#agname)
declare job_cursor cursor for
select s.name from msdb.dbo.sysjobs s
inner join msdb.dbo.syscategories c on s.category_id = c.category_id
where c.name = #agname
order by name
open job_cursor
fetch next from job_cursor into #job_name
while ##fetch_status = 0
begin
select #job_enabled=enabled from msdb.dbo.sysjobs where name = #job_name
if #is_primary_replicate = 1
begin
if #job_enabled = 1
print #job_name+' enabled on primary. do nothing'
else
begin
print #job_name+' disabled on primary. enable it !'
exec msdb.dbo.sp_update_job #job_name = #job_name,#enabled = 1
end
end
else if (#is_primary_replicate = 0)
begin
if #job_enabled = 1
begin
print #job_name+' enabled on secondary. disable it !'
exec msdb.dbo.sp_update_job #job_name = #job_name,#enabled = 0
end
else
print #job_name+' disabled on secondary. do nothing'
end
fetch next from job_cursor into #job_name
end
close job_cursor
deallocate job_cursor
END
GO
Also here is the function script:
ALTER FUNCTION dbo.fn_hadr_group_is_primary (#AGName sysname)
RETURNS bit
AS
BEGIN
DECLARE #PrimaryReplica sysname;
SELECT #PrimaryReplica = hags.primary_replica
FROM
sys.dm_hadr_availability_group_states hags
INNER JOIN sys.availability_groups ag ON ag.group_id = hags.group_id
WHERE
ag.name = #AGName;
IF UPPER(#PrimaryReplica) = UPPER(##SERVERNAME)
RETURN 1; -- primary
RETURN 0; -- not primary
END;
GO
However, when I execute on the secondary server like this:
exec master.dbo.SQLAgentJobFailover #agname = 'CorpAnalyticsAG'
It says the command completed successfully however the job isn't disabled.
I have no idea why.
Below is my AG name
Any ideas?
One of the problems is that the value you are setting your #job_name variable to is being over written in the cursor definition. Because it is not actually enabling or disabling the job the cursor definition is not likely returning the job you actually want to enable or disable within its results set which could be due most likely to #agname value that is being passed and/or the cursor select definition.
Because you only want to deal with one job you really don't need the cursor definition but you still need to test if the primary replica is the same as the ##SERVERNAME
Related
I need to disable all jobs running on a server. I ran the following code:
declare #t table (schedule_id int, [name] varchar(300))
update msdb.dbo.sysschedules
set enabled = 0
output inserted.schedule_id, inserted.name into #t
from msdb.dbo.sysjobs j
join msdb.dbo.sysjobschedules js
on j.job_id = js.job_id
join msdb.dbo.sysschedules s
on js.schedule_id = s.schedule_id
where j.[name] not like 'DB -%' and j.[name] not like 'sys%' and j.[name] not like 'Maintenance%'
select * from #t
When I check a given job and its associated schedule, either from the SQL Agent GUI, or through SQL, I can see the schedule is disabled. However, the jobs are still running, and I can see next run dates in the future if I check msdb.dbo.sysjobschedules.
The only things I've seen online suggest that there's a background thread that needs to be refreshed to pick up this change? And that do that, I need to run sp_sqlagent_refresh_job (did not help), or restart the server.
I can restart the server, but I'm curious if anyone else has seen this behavior, knows the cause, knows how to fix it another way.
Why are you bothering with schedules? Use sp_update_job with dynamic SQL to disable all jobs:
DECLARE #sql nvarchar(max)
;WITH cte AS (
SELECT j.job_id,
j.[name]
FROM msdb.dbo.sysjobs j
WHERE j.[name] not like 'DB -%'
and j.[name] not like 'sys%'
and j.[name] not like 'Maintenance%'
)
SELECT #sql = (
SELECT 'EXEC msdb.dbo.sp_update_job #job_id = '''+CAST(job_id as nvarchar(max))+''', #enabled = 0;'+CHAR(10)
FROM cte
FOR XML PATH('')
)
EXEC sp_executesql #sql
If you PRINT #sql before EXEC you will see generated query:
EXEC msdb.dbo.sp_update_job #job_id = 'CCCF8FC0-FCD4-4260-9A48-518AF5826297', #enabled = 0;
EXEC msdb.dbo.sp_update_job #job_id = '48BB41E6-6BEC-452B-8D42-760AECDBB808', #enabled = 0;
If you need to stop jobs use sp_stop_job.
How we can terminate or kill the suspended task automatically in SQL Server 2012? When I run sp_who2 then it shows many process in a Suspended state.
I want to know that is there any way that we can terminate or kill the suspended process/task automatically rather than doing manually.
I know how to kill the SPID manually but I want something which can automatically kill those tasks after a defined amount of time.
Any help will be appreciated.
Here is simple code snippet that shows all suspended SQL tasks and generates script to kill them all.
DECLARE #sql NVARCHAR(max) = ''
SELECT #sql = #sql + 'kill ' + cast(session_id as nvarchar(30)) + ';'
-- select *
FROM sys.dm_exec_requests
WHERE status = 'suspended'
--and database_id = DB_ID('master')
--and user_id = USER_ID('sa')
print #sql
--EXEC(#sql)
To add filter by DB uncomment following line and change DB name
and database_id = DB_ID('master')
You also can filter it by DB user who is running suspended query
and user_id = USER_ID('sa')
To execute that generated script automatically just uncomment last line
EXEC(#sql)
Should work on all SQL Server versions. Tested on Microsoft SQL Server 2008 R2 and higher
I have written a sample here:
DECLARE #sql NVARCHAR(max) = ''
SELECT #sql = CONCAT(#sql, 'kill ' , session_id, CHAR(13))
FROM sys.dm_exec_requests
WHERE status = 'suspended'
EXEC(#sql)
To do that, you should:
Query a special DMV to get PSIDs in SUSPENDED state:
SELECT * FROM sys.dm_exec_requests WHERE status = 'suspended'
Use e.g. cursor on that resultset to kill them one by one
Take that script and stuff it into the database job; schedule the job to run regularly
However, if you do that, you should have in mind that suspended query can start running again when resource he's trying to use gets freed/unlocked. And if that query is in e.g. stored procedure which modifies data without transaction, then your kill-automatization could screw up badly by interrupting such process(es).
DECLARE #ID AS BIGINT
SET #ID = 0
declare #str as nvarchar(Max)
this Code Work By Time in this Example :
DECLARE Spliter1_Cursor CURSOR FOR
SELECT SPID
FROM sys.sysprocesses
WHERE hostname ='APPSERVER'
AND datediff(second,last_batch,getdate()) > 999
ORDER BY datediff(second,last_batch,getdate()) DESC
OPEN Spliter1_Cursor FETCH NEXT FROM Spliter1_Cursor
INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SET#str = ''
SET#str = 'kill '+ cast (#id as nvarchar (max))
EXEC (#str)
FETCH NEXT FROM Spliter1_Cursor INTO #ID
END
CLOSE Spliter1_Cursor
DEALLOCATE Spliter1_Cursor
My code is to have a trigger that recognize when a job failed, sends an email and update our ticketing system using a stored procedure that is in a linked db.
The emailing part works perfect, but when adding the ticket procedure code the email wont send, the ticket will not open, and the specific line that was supposed to be in msdb.dbo.sysjobhistory is actually missing, like the insert failed or the line was deleted.
The procedure code line by itself works fine, and if i run the same code without the trigger part it also works fine.
The code works ok outside of the trigger, which makes me think I missing something regarding the way triggers work.
alter TRIGGER trig_open_ticket_failed
ON msdb.dbo.sysjobhistory
after INSERT
AS begin
DECLARE #MaxInstance int
Declare #failedFlag int
Declare #JobName varchar(50)
Declare #ErrorInfo varchar(max)
Declare #stepName varchar(50)
set #MaxInstance= (select MAX(instance_id)
from msdb.dbo.sysjobhistory
)
select top 1 #failedFlag = hs.run_status, #stepName=hs.step_name
from msdb.dbo.sysjobhistory hs join msdb.dbo.sysjobs sj
on hs.job_id=sj.job_id
where instance_id= #MaxInstance
if #failedFlag=0
BEGIN
SELECT TOP 1 #JobName= 'LKSQL job failed:' + SJ.name, #ErrorInfo=hs.message
from msdb.dbo.sysjobhistory hs join msdb.dbo.sysjobs sj
on hs.job_id=sj.job_id
where instance_id= #MaxInstance
set #errorInfo= 'Job: '+ #JobName +' Failed with error: '+ #errorInfo;
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'name',
#recipients = 'adrs#email.com',
#body = #ErrorInfo,
#subject = #JobName ;
exec [mainsql01].[SlickTicket].[dbo].[AddTicket] 'servicesFailed',#jobName, #ErrorInfo, '5','4' ;
END
end
GO
Thank you,
Idan.
your trigger will be executing as caller by default,which I am guessing does not have permission to use your addticket procedure. try adding
execute as user_name
where user_name has permission to use that proc
I want to modify the following as it doesn't seem to kill processes - I think its supposed to disconnect users (is this the same?). I want to be able to kill all process for a particular database - how can I modify the below:
create procedure [dbo].[sp_killusers](#database varchar(30))
as
----------------------------------------------------
-- * Created By David Wiseman, Updated 19/11/2006
-- * http://www.wisesoft.co.uk
-- * This procedure takes the name of a database as input
-- * and uses the kill statment to disconnect them from
-- * the database.
-- * PLEASE USE WITH CAUTION!!
-- * Usage:
-- * exec sp_killusers 'databasename'
----------------------------------------------------
set nocount on
declare #spid int
declare #killstatement nvarchar(10)
-- Declare a cursor to select the users connected to the specified database
declare c1 cursor for select request_session_id
from sys.dm_tran_locks
where resource_type='DATABASE'
AND DB_NAME(resource_database_id) = #database
open c1
fetch next from c1 into #spid
-- for each spid...
while ##FETCH_STATUS = 0
begin
-- Don't kill the connection of the user executing this statement
IF ##SPID <> #spid
begin
-- Construct dynamic sql to kill spid
set #killstatement = 'KILL ' + cast(#spid as varchar(3))
exec sp_executesql #killstatement
-- Print killed spid
print #spid
end
fetch next from c1 into #spid
end
-- Clean up
close c1
deallocate c1
Update
The above doesn't work i.e. it doesn't kill the process.
It doesn't kill the process. I look at
the activity monitor and its still
shows the process continuing and I can
see my query still working in the
query window. When I do "kill 53", the
querying stops in the query window and
the process is gone from the activity
monitor! So th kill works but not this procedure why?
Are you just trying to stop all activity on a particular DB so you can do some maintenance on it?
If so, you can do the following:
ALTER DATABASE myDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
This will kill all other SPIDs accessing the DB, and will put the DB in single-user mode. Then perform your maintenance action, and do the following afterwards:
ALTER DATABASE myDB SET MULTI_USER;
I'm familiar with this script. It kills all SPIDs that are using a database, yes. You need to run it under the correct permissions - not just any user can kill SPIDs.
Also, there's a chance you may have applications that try and maintain persistent connections to the DB, and therefore may reconnect shortly after you kill their SPID.
You might want to try using exec instead of sp_exec (not that it should make any difference)
SET #killstatement = 'KILL ' + cast(#spid as varchar(3))
EXEC (#killstatement)
Have you tried any debugging/output of what actually occurs when the procedure is run? For example, can you modify you #killstatement to be declared as nvarchar(max) and include some verbose output such as the following and post the results? Basically replace everything within your begin/end block with something like:
-- Construct dynamic sql to kill spid
select #killstatement = N'
select *
from sys.dm_exec_sessions s
join sys.dm_exec_connections c
on s.session_id = c.session_id
where c.session_id = #spid;
kill ' + cast(#spid as varchar(3)) + ';
select *
from sys.dm_exec_sessions s
join sys.dm_exec_connections c
on s.session_id = c.session_id
where c.session_id = #spid;
';
-- Print & Exec
print #killstatement;
exec sp_executesql #killstatement, N'#spid smallint', #spid;
print #spid;
There's no reason anything should be behaving differently within the procedure code vs. executing explicitly within a connection - assuming you have the appropriate permissions, are killing valid spids, etc., etc. If you can post the results of some debugging like the above (and anything else you may have tried), it would help figure out where the issue is. You might also want to include a debug output of the results of the cursor declare you are using to make sure you are actually getting the sessions you are trying to kill - i.e. simply include the same select you are using in your cursor declare to output a result set, like this:
declare c1 cursor for select request_session_id
from sys.dm_tran_locks
where resource_type='DATABASE'
AND DB_NAME(resource_database_id) = #database
-- Debug output - sessions we should try and kill...
select request_session_id
from sys.dm_tran_locks
where resource_type='DATABASE'
AND DB_NAME(resource_database_id) = #database;
If you can post the results, hopefully that will give us something to go on.
Odds are good that none of these apply to you, but just in case here are some oddball situations I encountered when working on stuff like this a few years back (all SQL 2005).
You can't kill your own connection.
In the code I used, I made sure to never try and kill any spid under 51. (These are system connections; I don't know if they can be killed, but I wouldn't try it.)
If a connection is processing a transaction, it has to roll that transaction back before it can be killed. Huge transactions can take significant time to roll back.
Beware connection pooling. They're like the undead--kill them, and they just come right back, often in under a second.
Running SQL Profiler and tracking logins and logouts while you run this process might be revealing, particularly for connection pooling issues.
This works for me in SQLServer 2000
DECLARE #DbName VARCHAR(100)
DECLARE #SPID INT
DECLARE #TranUOW UNIQUEIDENTIFIER
DECLARE #KillStmt NVARCHAR(100)
SET #DbName = 'MyDatabase'
-----------------------------------
-- Kill distributed transactions
DECLARE dist CURSOR FOR
SELECT DISTINCT req_transactionUOW
FROM master..syslockinfo
WHERE db_name(rsc_dbid) = #DbName
AND req_transactionUOW <> '00000000-0000-0000-0000-000000000000'
OPEN dist
FETCH NEXT FROM dist INTO #TranUOW
WHILE ##FETCH_STATUS = 0
BEGIN
SET #KillStmt = 'kill ''' + CAST(#TranUOW AS VARCHAR(50)) + ''''
PRINT #KillStmt
EXECUTE(#KillStmt)
FETCH NEXT FROM dist INTO #TranUOW
END
CLOSE dist
DEALLOCATE dist
-----------------------------------
-- Kill user connections
DECLARE cur CURSOR FOR
SELECT spid
FROM master..sysprocesses
WHERE db_name(dbid) = #DbName
AND spid > 50
OPEN cur
FETCH NEXT FROM cur INTO #SPID
WHILE ##FETCH_STATUS = 0
BEGIN
SET #KillStmt = 'kill ' + CAST(#SPID AS VARCHAR(10))
PRINT #KillStmt
EXECUTE(#KillStmt)
FETCH NEXT FROM cur INTO #SPID
END
CLOSE cur
DEALLOCATE cur
i want to ensure that all stored procedures are still syntactically valid. (This can happen if someone renames/deletes a table/column).
Right now my solution to check the syntax of all stored procedures is to go into Enterprise Manager, select the first stored procedure in the list, and use the procedure:
Enter
Alt+C
Escape
Escape
Down Arrow
Goto 1
It works, but it's pretty tedious. i'd like a stored procedure called
SyntaxCheckAllStoredProcedures
like the other stored procedure i wrote that does the same thing for views:
RefreshAllViews
For everyone's benefit, RefreshAllViews:
RefreshAllViews.prc
CREATE PROCEDURE dbo.RefreshAllViews AS
-- This sp will refresh all views in the catalog.
-- It enumerates all views, and runs sp_refreshview for each of them
DECLARE abc CURSOR FOR
SELECT TABLE_NAME AS ViewName
FROM INFORMATION_SCHEMA.VIEWS
OPEN abc
DECLARE #ViewName varchar(128)
-- Build select string
DECLARE #SQLString nvarchar(2048)
FETCH NEXT FROM abc
INTO #ViewName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLString = 'EXECUTE sp_RefreshView '+#ViewName
PRINT #SQLString
EXECUTE sp_ExecuteSQL #SQLString
FETCH NEXT FROM abc
INTO #ViewName
END
CLOSE abc
DEALLOCATE abc
For everyone's benefit, a stored procedure to mark all stored procedure as needing a recompile (marking a stored procedure for recompile will not tell you if it's syntactically valid):
RecompileAllStoredProcedures.prc
CREATE PROCEDURE dbo.RecompileAllStoredProcedures AS
DECLARE abc CURSOR FOR
SELECT ROUTINE_NAME
FROM INFORMATION_SCHEMA.routines
WHERE ROUTINE_TYPE = 'PROCEDURE'
OPEN abc
DECLARE #RoutineName varchar(128)
-- Build select string once
DECLARE #SQLString nvarchar(2048)
FETCH NEXT FROM abc
INTO #RoutineName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLString = 'EXECUTE sp_recompile '+#RoutineName
PRINT #SQLString
EXECUTE sp_ExecuteSQL #SQLString
FETCH NEXT FROM abc
INTO #RoutineName
END
CLOSE abc
DEALLOCATE abc
For completeness sake, the UpdateAllStatistics procedure. This will update all statistics in the database by doing a full data scan:
RefreshAllStatistics.prc
CREATE PROCEDURE dbo.RefreshAllStatistics AS
EXECUTE sp_msForEachTable 'UPDATE STATISTICS ? WITH FULLSCAN'
You can also do this "in-place" - without getting all the create statements.
In addition to setting NOEXEC ON, you will also need to set your favorite SHOWPLAN_* ON (I use SHOWPLAN_TEXT). Now you can get rid of your step 2 and just execute each procedure you retrieved in step 1.
Here is a sample using an individual stored procedure. You can work it into your favorite loop:
create procedure tests #bob int as
select * from missing_table_or_view
go
set showplan_text on;
go
set noexec on
exec tests
set noexec off
go
set showplan_text off;
go
drop procedure tests
go
The above sample should generate the following output:
Msg 208, Level 16, State 1, Procedure tests, Line 2
Invalid object name 'missing_table_or_view'.
The check suggested by KenJ is definitely the best one, since the recreate/alter-approaches does not find all errors. E.g.
impossible execution plans due to query-hints
I even had an SP referencing a non-existing table that went through without the error being detected.
Please find my version that checks all existing SPs at once with KenJ's method below. AFAIK, it will detect every error that will keep the SP from being executed.
--Forces the creation of execution-plans for all sps.
--To achieve this, a temporary SP is created that calls all existing SPs.
--It seems like the simulation of the parameters is not necessary. That makes things a lot easier.
DECLARE #stmt NVARCHAR(MAX) = 'CREATE PROCEDURE pTempCompileTest AS ' + CHAR(13) + CHAR(10)
SELECT #stmt = #stmt + 'EXEC [' + schemas.name + '].[' + procedures.name + '];'
FROM sys.procedures
INNER JOIN sys.schemas ON schemas.schema_id = procedures.schema_id
WHERE schemas.name = 'dbo'
ORDER BY procedures.name
EXEC sp_executesql #stmt
GO
--Here, the real magic happens.
--In order to display as many errors as possible, XACT_ABORT is turned off.
--Unfortunately, for some errors, the execution stops anyway.
SET XACT_ABORT OFF
GO
--Showplan disables the actual execution, but forces t-sql to create execution-plans for every statement.
--This is the core of the whole thing!
SET SHOWPLAN_ALL ON
GO
--You cannot use dynamic SQL in here, since sp_executesql will not be executed, but only show the string passed in in the execution-plan
EXEC pTempCompileTest
GO
SET SHOWPLAN_ALL OFF
GO
SET XACT_ABORT ON
GO
--drop temp sp again
DROP PROCEDURE pTempCompileTest
--If you have any errors in the messages-window now, you should fix these...
If you are using sql 2008 r2 or below then do not use
SET NOEXEC ON
It only checks the syntax and not for potential errors like the existence of tables or columns.
Instead use:
SET FMTONLY ON
it will do a full compile as it tries to return the meta data of the stored procedure.
For 2012 and you will need to use stored procedure:
sp_describe_first_result_set
Also you can do a complete script in Tsql that checks all sp and views, its just a bit of work.
UPDATE
I wrote a complete solution for in tsql that goes through all user defined stored proceedures and checks there syntax. the script is long winded but can be found here http://chocosmith.wordpress.com/2012/12/07/tsql-recompile-all-views-and-stored-proceedures-and-check-for-error/
In addition you might want to consider using Visual Studio Team System 2008 Database Edition which, among other things, does a static verification of all stored procedures in the project on build, thus ensuring that all are consistent with the current schema.
I know this is way old, but I created a slightly different version that actually re-creates all stored procedures, thus throwing errors if they cannot compile. This is something you do not achieve by using the SP_Recompile command.
CREATE PROCEDURE dbo.UTL_ForceSPRecompilation
(
#Verbose BIT = 0
)
AS
BEGIN
--Forces all stored procedures to recompile, thereby checking syntax validity.
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SPName NVARCHAR(255)
DECLARE abc CURSOR FOR
SELECT NAME, OBJECT_DEFINITION(o.[object_id])
FROM sys.objects AS o
WHERE o.[type] = 'P'
ORDER BY o.[name]
OPEN abc
FETCH NEXT FROM abc
INTO #SPName, #SQL
WHILE ##FETCH_STATUS = 0
BEGIN
--This changes "CREATE PROCEDURE" to "ALTER PROCEDURE"
SET #SQL = 'ALTER ' + RIGHT(#SQL, LEN(#SQL) - (CHARINDEX('CREATE', #SQL) + 6))
IF #Verbose <> 0 PRINT #SPName
EXEC(#SQL)
FETCH NEXT FROM abc
INTO #SPName, #SQL
END
CLOSE abc
DEALLOCATE abc
END
I know this is a old question but this is my solution when I could not find any suiting.
I required to validate my stored procedures and views after alot of changes in the database.
Basicly what i wanted was to try to do a ALTER PROCEDURE and ALTER VIEW using the current procedures and view (not actually changing them).
I have written this that works fairly well.
Note! Do not perform on live database, make a copy to validate and then fix the things need fixing. Also sys.sql_modules can be inconsistent so take extra care. I do not use this to actually make the changes, only to check which are not working properly.
DECLARE #scripts TABLE
(
Name NVARCHAR(MAX),
Command NVARCHAR(MAX),
[Type] NVARCHAR(1)
)
DECLARE #name NVARCHAR(MAX), -- Name of procedure or view
#command NVARCHAR(MAX), -- Command or part of command stored in syscomments
#type NVARCHAR(1) -- Procedure or view
INSERT INTO #scripts(Name, Command, [Type])
SELECT P.name, M.definition, 'P' FROM sys.procedures P
JOIN sys.sql_modules M ON P.object_id = M.object_id
INSERT INTO #scripts(Name, Command, [Type])
SELECT V.name, M.definition, 'V' FROM sys.views V
JOIN sys.sql_modules M ON V.object_id = M.object_id
DECLARE curs CURSOR FOR
SELECT Name, Command, [Type] FROM #scripts
OPEN curs
FETCH NEXT FROM curs
INTO #name, #command, #type
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
IF #type = 'P'
SET #command = REPLACE(#command, 'CREATE PROCEDURE', 'ALTER PROCEDURE')
ELSE
SET #command = REPLACE(#command, 'CREATE VIEW', 'ALTER VIEW')
EXEC sp_executesql #command
PRINT #name + ' - OK'
END TRY
BEGIN CATCH
PRINT #name + ' - FAILED: ' + CAST(ERROR_NUMBER() AS NVARCHAR(MAX)) + ' ' + ERROR_MESSAGE()
--PRINT #command
END CATCH
FETCH NEXT FROM curs
INTO #name, #command, #type
END
CLOSE curs
A bit of a drawn-out option:
Create a copy of the database
(backup and restore). You could do this on the target database, if your confidence level is high.
Use SSMS to script out all the
stored procedures into a single script file
DROP all the procedures
Run the script to recreate them. Any that can't be created will error out.
Couple of fussy gotchas in here, such as:
You want to have the "if proc exists
then drop proc GO create proc ... GO"
syntax to separte each procedure.
Nested procedures will fail if they
call a proc that has not yet been
(re)created. Running the script several
times should catch that (since
ordering them properly can be a real
pain).
Other and more obscure issues might crop up, so be wary.
To quickly drop 10 or 1000 procedures, run
SELECT 'DROP PROCEDURE ' + schema_name(schema_id) + '.' + name
from sys.procedures
select the output, and run it.
This assumes you're doing a very infrequent task. If you have to do this regularly (daily, weekly...), please let us know why!
There is no way to do it from T-SQL, or Enterprise Manager, so i had to write something from client code. i won't post all the code here, but the trick is to:
1) Get a list of all stored procedures
SELECT ROUTINE_NAME AS StoredProcedureName
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE' --as opposed to a function
ORDER BY ROUTINE_NAME
2) Get the stored procedure create T-SQL:
select
c.text
from dbo.syscomments c
where c.id = object_id(N'StoredProcedureName')
order by c.number, c.colid
option(robust plan)
3) Run the create statement with NOEXEC on, so that the syntax is checked, but it doesn't actually try to create the stored procedure:
connection("SET NOEXEC ON", ExecuteNoRecords);
connection(StoredProcedureCreateSQL, ExecuteNoRecords);
connection("SET NOEXEC ON", ExecuteNoRecords);
Here is an amendment which deals with multiple schemas
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RefreshAllViews] AS
-- This sp will refresh all views in the catalog.
-- It enumerates all views, and runs sp_refreshview for each of them
DECLARE abc CURSOR FOR
SELECT TABLE_SCHEMA+'.'+TABLE_NAME AS ViewName
FROM INFORMATION_SCHEMA.VIEWS
OPEN abc
DECLARE #ViewName varchar(128)
-- Build select string
DECLARE #SQLString nvarchar(2048)
FETCH NEXT FROM abc
INTO #ViewName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLString = 'EXECUTE sp_RefreshView ['+#ViewName+']'
PRINT #SQLString
EXECUTE sp_ExecuteSQL #SQLString
FETCH NEXT FROM abc
INTO #ViewName
END
CLOSE abc
DEALLOCATE abc
GO