msdb.dbo.sp_start_job got stuck in stored procedure - sql-server

I have a stored procedure USP_A with logic like following:
......
exec dbo.usp_Log 'Start to run job job1'
Exec #intErrorCode = msdb.dbo.sp_start_job 'job1'
IF #intErrorCode <> 0 Goto errorHandling
exec dbo.usp_Log 'End to run job job1'
......
But when I run this stored procedure, it got stuck and when I check log I can only see message 'Start to run job job1'. Also in the SQL Server Agent job monitor I can not see this job get triggered.
But if I manually run
Exec #intErrorCode = msdb.dbo.sp_start_job 'job1'
it works fine.
The SQL Server is Microsoft SQL Server 2005 Enterprise Edition (version 9.00.5000.00)

It turns out to be some kind of SQL Server bug (Maybe the SQL Server 2005 is quite old). The stored procedure just ended unexpectedly and does not return any code.
So the problem is solved by moving all the logic before this msdb.dbo.sp_start_job procedure into a separate stored procedure.
Hope it can help anyone who got this same issue.

Your job is probably failing to start and branching to the errorHandlingsection of your code. If you add ##ERROR_MESSAGE to the log first, then you will see the problem in your log. Something like:
exec dbo.usp_Log 'Start to run job job1'
begin try
Exec #intErrorCode = msdb.dbo.sp_start_job 'job1'
end try
begin catch
exec dbo.usp_Log 'Error: ' + ERROR_MESSAGE()
Goto errorHandling
end catch
exec dbo.usp_Log 'End to run job job1'
UPDATE
I ran a test on my server. I tried to initiate a job that was already running and an error was returned, but did not trigger the catch block. Apparently this is a problem with sp_start_job. This may be the source of some of your confusion. There is a workaround posted here: TRY/CATCH does not work on SQL Server Agent error?
My best guess though is that there is a permission issue when the code is run under a different user context. You could wrap the code to run the job into a procedure that executes under a different security context... like this:
create procedure dbo.sp_RunJob1
with execute as owner
as
exec sp_start_job #job_name = 'job1'
Then call the sp_RunJob1 instead of the sp_start_job statement in your code.

Related

How do I look at SQL Agent Job without starting SQL Agent?

I have just set up a new SQL Server instance on a new server and moved our application to use the new server. So I've had to turn SQL Agent off on the old server - turning it on would start the scheduler and start sending out emails and running things that shouldn't be run any more.
However, I need to take a close look at a SQL Agent Job on the old server, and ideally reverse-engineer the code to recreate it so I can modify it and apply it to the new server.
How do I generate the code for that Job on the old server without turning SQL Agent on?
Thanks
Even if SQL server agent is not running, you can see how jobs and schedules were set up by viewing the following system DMVs.
msdb.dbo.sysjobs_view
msdb.dbo.sysjobs
msdb.dbo.sysjobschedules
msdb.dbo.sysschedules
I use preset scripts to create all my jobs and schedules independent of the server. Here is a sample script to create the recycle log job. You can modify this or use any piece of this as you see fit.
DECLARE #sql nvarchar(max)
BEGIN TRY
IF EXISTS (SELECT job_id
FROM msdb.dbo.sysjobs_view
WHERE name = N'Cycle SQL log')
EXEC msdb.dbo.sp_delete_job #job_name=N'Cycle SQL log'
, #delete_unused_schedule=1
EXEC msdb.dbo.sp_add_job
#job_name = N'Cycle SQL log',
#description = N'This job forces SQL to start a new error log (In the Managment node of SSMS)',
#owner_login_name = N'your_sql_login' ;
EXEC msdb.dbo.sp_add_jobstep
#job_name = N'Cycle SQL log',
#step_name = N'sp_cycle_errorlog',
#subsystem = N'TSQL',
#command = N'exec sp_cycle_errorlog' --put your executable code here
--These next two lines set the target server to local, so that the job can be modified if necessary
SET #sql = 'EXEC msdb.dbo.sp_add_jobserver #job_name=N''Cycle SQL Log'', #server_name = N''' + ##SERVERNAME + ''''
EXEC sys.sp_executesql #stmt = #sql
END TRY
BEGIN CATCH
PRINT 'Uh-oh. Something bad happened when creating the Cycle SQL Log job. See the following error.'
PRINT CAST(ERROR_MESSAGE() AS NVARCHAR(1000))
END CATCH
You can use use code to automate the addition of schedules based on values you pull from the DMVs listed above.

TSQL - Only execute a line if run manually (not in job)

Can I make an IF statement that only executes when run manually from SSMS?
I have a SQL Server job that executes TSQL code. That TSQL code is maintained in a separate .sql text file. When it needs to be edited, I edit the text file and copy&paste the final results into the job.
This normally works very well but there is one critical line that is only used for testing (it sets a variable to a specific value). How can I guarantee that line only executes when run manually?
Is there something like If ManualExecution() then Blah?
IF APP_NAME() LIKE 'Microsoft SQL Server Management Studio%'
BEGIN
PRINT 'Running inside SSMS'
END;
If you use SQL Agent to run the job, it's app name should be SQLAgent - TSQL JobStep (Job 0x... : Step ...). If you use some other software, just make sure that it doesn't set its Application Name to "Microsoft SQL Server Management Studio"...
You can use the following code to get the SQL Server Agent JobId of the current process (otherwise NULL):
declare #JobId as UniqueIdentifier;
begin try
-- The executed statement will result in a syntax error if not in a SQL Server Agent job.
execute sp_executesql
#stmt = N'select #JobId = Cast( $(ESCAPE_NONE(JOBID)) as UniqueIdentifier );',
#params = N'#JobId UniqueIdentifier output',
#JobId = #JobId output;
end try
begin catch
if ##Error != 102 -- 102 = Syntax error.
select ##Error; -- Handle unexpected errors here.
end catch
select #JobId as JobId; -- NULL if not running as a SQL Server Agent job.
Note that the JobId can be used to access additional information about the current job in dbo.sysjobs.

SSIS package execution succeeds but doesn't do its job

I use SQL Server Agent to fill tables in DataWarehouse. I have about 50 steps in the job and every step is run with a proxy account. And every step works correctly besides one:
SSIS package which contains about 20 Execute SQL Tasks which execute procedure. This is what I have in the Execute SQL Task:
DECLARE #RC int
DECLARE #fordate datetime = null
DECLARE #tablename sysname = 'D_ENTITY'
DECLARE #dataFolder varchar(1024) = 'C:\MountPoints1\src_etl\'
DECLARE #logFolder varchar(1024) = 'C:\MountPoints1\src_etl\'
DECLARE #debug bit = 0
EXECUTE #RC = [dbo].[ETL1A_HR]
#fordate
,#tablename
,#dataFolder
,#logFolder
,#debug
GO
The thing is, that if I execute the package from the SSIS catalog, it works ok. But if it is run by job, it succeeds, but only deletes from tables, but doesn't fill it. It seems like the procedure stops somewhere in the middle.
Any ideas?
Please advise, it took me days trying to solve this...
I think it maybe related to permissions. Executing the SSIS package will use your security context but running it from the agent impersonates the credentials defined in the proxy, and then runs the job step by using that security context.

sql server cannot create stored procedure sp_start_job

I have a job I need to launch from a SP. To do that I need a SP in my msdb system database called sp_start_job. When I try to execute this code in SSMS:
Use msdb
go
CREATE PROCEDURE [dbo].[sp_start_job]
[#job_name]
[,#error_flag ]
[,#server_name]
[,#step_name ]
[,#output_flag ]
I get this error:
Incorrect syntax near '#job_name'.
This is all from the SQL books on line. So what am I doing wrong?
You don't need to create the sp_start_job sproc, it should already exist in your msdb database.
You need to execute the stored procedure using the relevant parameters. For example:
EXEC msdb.dbo.sp_start_job
'Your Job Name'; -- Add any other arguments if you need them
See the MSDN documentation page for further info.

How do I get SQL Server Management Studio to stop processing on an error?

This seems like an incredibly dumb question to have to ask, but how do I get SQL Server Management Studio to stop processing a SQL script when it encounters an error?
I have a long script and if there is an error at the start SSMS reports it and then blindly carries on, screwing up things even more. I can't use transactions because the script creates several databases and they can't go in a transaction. It is the database creation that sometimes fails.
Toad for SQL Server will pause at the first error it encounters and ask if you want to stop or continue. This is the behaviour I'm looking for. Does it exist in SSMS?
I am not asking, "How do I write or modify a script so that it stops on an error?" I'm not interested in modifying my script to make this happen, I just want SSMS to stop on an error. Toad for SQL Server does exactly this and that is the behaviour I want. This is also not a duplicate of 659188 because that relates to modifying the script to stop SSMS.
Short answer: You can't.
Thanks to those that provided workarounds, but it seems that SSMS itself can not be set to pause or stop on an error in the same way that Toad for SQL Server can.
consider using the command line program 'sqlcmd' that comes with SQL Server, with the -b and the -V options set. -b will cause sqlcmd to quit when it hits an error. -V controls the severity level that is considered to be an error.
ApexSQL Script generates batch scripts in exactly the manner you want. As an example:
--Script Header
begin transaction
go
{Statement #1}
go
--Standard Post-Statement Block
if ##error <> 0 or ##trancount = 0 begin
if ##trancount > 0 rollback transaction
set noexec on
end
go
{Statement #2}
go
--Standard Post-Statement Block
if ##error <> 0 or ##trancount = 0 begin
if ##trancount > 0 rollback transaction
set noexec on
end
go
--Script Footer
if ##trancount > 0 commit transaction
go
set noexec off
go
11 years later SSMS still doesn't have this feature...
BUT! You can enable SQLCMD mode (Menu/Query/SQLCMD Mode) and then in text editor you can define this option:
:ON ERROR EXIT
before your t-sql script. Now it will stop execution on error.
You need to wrap your SQL Statements inside a Transaction.
BEGIN TRANSACTION
/* run all your SQL statements */
COMMIT TRANSACTION
If there's an error inside the begin/end transaction, all statements will be rolled back.
EDIT: Wrapping inside inside begin/end transaction, will prevent the statements from getting committed to the database, but not stop it at that point. You need to additionally wrap it inside a try/catch block as follows:
BEGIN TRY
BEGIN TRANSACTION
/* run all your SQL statements */
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
Wow. That's kinda rubbish isn't it? I use SQL-Workbench which, like Toad for SQL Server, handles this easily. Unlike Toad for SQL Server though, it's free.
I'm astonished that such fundamental functionality isn't part of the standard tool.
would using a try catch block help here. On error the try will be exited, implement error handling in the catch
http://msdn.microsoft.com/en-us/library/ms179296.aspx
There are a few more work-arounds mentioned here:
SQL Server - stop or break execution of a SQL script
and
SQL Server: How to abort a series of batches in Query Analyzer?
(raiseerror 20 with log, set noexec on, sqlcmd mode :on error exit, etc.)
If you can't put your script into a stored procedure and use the return statement to exit on error, the solution provided by #Justice might be your best bet. Everyone else is missing the point - you can't return from a script, even if you use transactions or even if you raiserror. SSMS will just execute the next thing anyway, even if set xact abort is on.
If you can convert your script to a stored procedure, then you can just return from it when you detect an error.

Resources