In below scenario, I'm trying to import hundreds of procedures into other database.
Solution: SQL Server
Source
SERVER: A
DATABASE: Apple
PROCEDURES: SP1, SP2, SP3 ... SP100
Destination
SERVER: B
DATABASE: Orange
First thing I did was find only non-existing procedures when compared to both databases.
To do so, I used INFORMATION_SCHEMA.ROUTINES from each database and compared in Excel.
After finding a list of procedures to be imported, I wanted to import all procedures at once.
However, if I create procedure in one-lined text, it will be saved one line which has zero visibility.
So, I used sp_helptext to copy by line for each. Then, created little query like below:
create table #proceduretext (runquery varchar(max))
insert into #proceduretext
exec sp_helptext 'SP1'
insert into #proceduretext select 'go'
insert into #proceduretext
exec sp_helptext 'SP2'
insert into #proceduretext select 'go'
insert into #proceduretext
exec sp_helptext 'SP3'
insert into #proceduretext select 'go'
.
.
.
insert into #proceduretext
exec sp_helptext 'SP100'
insert into #proceduretext select 'go'
Then do a select from temp table, paste result, run.
However, above was still insufficient.
Please help with below questions:
Is there a way to use INFORMATION_SCHEMA.ROUTINES for linked servers?
If there is an answer to question 1, how can I loop below query for all missing procedures?
insert into #proceduretext
exec sp_helptext 'SP100'
insert into #proceduretext select 'go'
You can do that with a linked server pretty easily. Here's an example I prepared for you. Run it from within the target database and change the [GREGT580] to whatever your linked server name is.
USE tempdb;
GO
-- Copying from [GREGT580] to local server
SET NOCOUNT ON;
DECLARE #MissingProcedures TABLE
(
MissingProcedureID int IDENTITY(1,1) PRIMARY KEY,
SchemaName sysname,
ProcedureName sysname,
ProcedureDefinition nvarchar(max)
);
INSERT #MissingProcedures
(
SchemaName, ProcedureName, ProcedureDefinition
)
SELECT s.[name], p.[name], sm.definition
FROM [GREGT580].AdventureWorks.sys.procedures AS p
INNER JOIN [GREGT580].AdventureWorks.sys.schemas AS s
ON p.schema_id = s.schema_id
INNER JOIN [GREGT580].AdventureWorks.sys.sql_modules AS sm
ON sm.object_id = p.object_id
WHERE NOT EXISTS (SELECT 1
FROM sys.procedures AS pl
INNER JOIN sys.schemas AS sl
ON sl.schema_id = pl.schema_id
AND sl.[name] = s.[name] COLLATE DATABASE_DEFAULT
AND pl.[name] = p.[name] COLLATE DATABASE_DEFAULT);
DECLARE #SchemaName sysname;
DECLARE #ProcedureName sysname;
DECLARE #ProcedureDefinition nvarchar(max);
DECLARE #Counter int = 1;
WHILE #Counter < (SELECT MAX(MissingProcedureID) FROM #MissingProcedures AS mp)
BEGIN
SELECT #SchemaName = mp.SchemaName,
#ProcedureName = mp.ProcedureName,
#ProcedureDefinition = mp.ProcedureDefinition
FROM #MissingProcedures AS mp
WHERE mp.MissingProcedureID = #Counter;
PRINT #ProcedureDefinition; -- Change to EXEC (#ProcedureDefinition) to create
SET #Counter += 1;
END;
I made the code just print out the procedures, so you could test it. Change the PRINT line to the EXEC option shown when you want to actually created them. (Note that PRINT will truncate what it shows if they are long but EXEC will be fine).
Hope that helps you.
try using https://www.red-gate.com/ compare tool you can download a trail version. Works great
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
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.
The database user has been granted SQLAgentOperatorRole, but there are still error when set job enable/disable. The error message is:
SELECT permission was denied on the object 'sysjobs', database 'msdb', schema 'dbo'
We set the job enable/disable by sp_update_job.
Comments:
When executing the following statement, will get the error.
bool result = ObjectInstance.ExecuteStoreQuery<bool>("EXEC usp_prod_SetJobStatus #jobName={0}, #isEnable={1}", jobName, isEnable).FirstOrDefault();
But, while use the following statement, it will execute successful.
ObjectInstance.ExecuteStoreCommand("EXEC msdb.dbo.sp_update_job #job_name={0}, #enabled={1}", jobName, isEnable);
usp_prod_SetJobStatus SP:
CREATE PROCEDURE [dbo].[usp_prod_SetJobStatus]
#jobName VARCHAR(200),
#isEnable BIT
AS
BEGIN
DECLARE #jobId uniqueidentifier
DECLARE #result BIT
SELECT #jobId = job_id FROM msdb.dbo.sysjobs WHERE name = #jobName
IF(#jobId IS NOT NULL)
BEGIN
EXEC #result = msdb.dbo.sp_update_job #job_name=#jobName, #enabled=#isEnable
END
ELSE
BEGIN
SET #result = 1
END
SELECT #result
END
I have solved this problem. The reason is that there is no SELECT permission on sysjobs table for the user. So, we need to grant the SELECT perssion for the user.
USE msdb
GRANT SELECT ON msdb.dbo.sysjobs TO useName
You have given agent operator role its having enable and disable but SQLAgentOperatorRole members can enable or disable local jobs they do not own by using the stored procedure sp_update_job and specifying values for the #enabled and the #job_id (or #job_name) parameters. If a member of this role specifies any other parameters for this stored procedure, execution of the procedure will fail ..You have to execute permissions on sql server agnet .
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