Selecting from stored procedure output - sql

I am trying to explore the possibility of selecting from a stored procedure.
Something like this
SELECT name
FROM exec msdb..sp_help_job
WHERE name = 'SampleJob'
I understand SQL Server - SELECT FROM stored procedure that a user-defined function or view can be used, but these are not options for me.
Reason being I am not able to run the following SQL statement due to permission limitations on AWS-RDS.
SELECT name as Jobs
FROM msdb..sysjobs
This leaves me with no choice but to use msdb..sp_help_job.
What I am ultimately trying to achieve is this "If job is not created, then run create job script". The reason I need to select from the stored procedure is to see if the job exists.
Appreciate any advice / directions.

If you want to create something, but are concerned that it might already exist, then use try/catch blocks.
begin try
exec dbo.sp_add_job . . .
end try
begin catch
print 'Error encountered . . . job probably already exists'
end catch;
To be honest, I haven't done this with jobs/job steps. However, this is one way of re-creating tables, views, and so on.

According to the documentation for sp_help_job on MSDN this stored procedure has a #job_name parameter and a simple return code (0 = success or 1 = failure).
If you set the #job_name parameter on your call to sp_help_job and get the return code you should be able to test the value of the return code to accomplish what you want.
Something like this should work:
DECLARE #return_value int
EXEC #return_value = msdb..sp_help_job #job_name = 'MyJobName'
-- #return_value = 1 means the specified #job_name does not exist
IF #return_value = 1
BEGIN
-- run create job script
END

Related

Stored procedure with parameter validation for job name

I would like to create a stored procedure in SQL that executes only jobs that are specified in the parameter validation of the procedure itself. For example I would like my dev team to only be able to pass the job names that I specify that way they will be able to run the jobs they need only. From their perspective it will look something like this.
Exec sp_run_only_jobs myjob
or
Exec sp_run_only_jobs myotherjob
if they try to run a job other than the two above it should fail with an error message.
I
You can create a stored procedure to check if the specified job name falls under a specific list of jobs, then execute the job. Otherwise, throw error message.
CREATE PROCEDURE dbo.usp_run_only_jobs #JobName NVARCHAR(4000)
AS
BEGIN
IF #JobName EXISTS IN ('AllowedJob1', 'AllowedJob2')
BEGIN
EXEC msdb.dbo.sp_start_job #JobName ;
END
ELSE
BEGIN
THROW 51000, 'The specified job is not allowed to be started', 1;
END
END

How to stop sequence of SQL stored procedure execution if one among them fails?

Here is one scenario i came across,
- I have a SQL job which has around four SQL stored procedure
- These are getting executed sequentially one after another
- Now Case is : If any of the stored procedure fails or raise an exception, entire Job should get halt.
How I could do that ?
Make yourself a little procedure with something like this:
BEGIN TRY
DECLARE #Return INTEGER
-- Run first procedure
EXEC #Return = firstProcedure
IF (#Return <> 0)
BEGIN
-- Do some error handling
END
-- Run second procedure
EXEC #Return = secondProcedure
IF (#Return <> 0)
BEGIN
-- Do some error handling
END
-- etc...
END TRY
BEGIN CATCH
-- Do some error handling
END CATCH
Although there are several different ways to do this, I would suggest that you use the facilities in SQL Server Agent. Make each of the calls a separate step in the job.
This will allow you to move from one step to the next when successful. You'll also be able to use SQL Server Agent's logging and error handling mechanisms to determine the error and handle it.

Customizable database names and TempDB

I have a lump of SQL that looks a little like this
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '{FOO}')
BEGIN
EXECUTE ('CREATE DATABASE {FOO}')
ALTER DATABASE {FOO} SET AUTO_CLOSE OFF
END
{FOO} is replaced at runtime with the name of a user configurable database. The logic is that I don't want to create the database if it already exists.
If {FOO} is tempdb then I get a failure when the query runs
Option 'AUTO_CLOSE' cannot be set in database 'tempdb'.
My question is why do I get this failure? SELECT * FROM sys.databases WHERE name = 'tempdb' returns zero results so surely my whole BEGIN/END pair shouldn't run? Indeed, if I put a print statement between begin and end, I don't see any output.
My guess is that SQL Server is doing some kind of linting on the SQL to make sure I don't muck around with tempdb? I have solved the problem by using EXECUTE instead, but I'm a little confused why I have to!
Try ensuring both commands are separate and within dynamic SQL, then the change to tempdb won't be caught by the parser:
EXEC sp_executesql N'CREATE DATABASE {FOO};';
EXEC sp_executesql N'ALTER DATABASE {FOO} SET AUTO_CLOSE OFF;';
This is similar to the reason you can't do this:
IF 1 = 1
BEGIN
CREATE TABLE #t1(id INT);
END
ELSE
BEGIN
CREATE TABLE #t1(x NVARCHAR(255));
END
Even though you and I know that only one of those #t1 code paths will ever be reached, SQL Server presumes that both paths could be reached at runtime, and so complains at parse time.

T-SQL Dynamically execute stored procedure

I have a logging function in T-SQl similiar to this:
CREATE PROCEDURE [logging]
#PROCEDURE VARCHAR(50),
#MESSAGE VARCHAR(MAX)
AS
BEGIN
PRINT #MESSAGE
END;
GO
I am able to call it like this:
execute logging N'procedure_i_am_in', N'log_message';
As my stored procedure names are a bit long winded, I want to write an alias or an inline function or so, to call the logging procedure for me, with the current procedure. Something like this (which is broken):
declare #_log varchar(max)
set #_log = 'execute logging N''procedure_i_am_in'', '
execute #_log N'MESSAGE!'
And i would put that alias at the top of each procedure.
What are your thoughts?
Quite simple
CREATE PROCEDURE [logging]
#PROCID int,,
#MESSAGE VARCHAR(MAX)
-- allows resolution of #PROCID in some circumstances
-- eg nested calls, no direct permission on inner proc
WITH EXECUTE AS OWNER
AS
BEGIN
-- you are using schemas, right?
PRINT OBJECT_SCHEMA_NAME(#PROCID) + '.' + OBJECT_NAME(#PROCID);
PRINT #MESSAGE
END;
GO
Then
execute logging ##PROCID, N'log_message';
MSDN on OBJECT_SCHEMA_NAME and ##PROCID
Edit:
Beware of logging into tables during transactions. On rollback, you'll lose the log data
More trouble than it's worth, but
it would be
Set #_log = 'exec ....N' + 'MESSAGE!'
Exec (#log)
So not a lot of use.
Personally I'sd just rename the SP, or at a push use a tersely named function. Building strings and exec'ing them is an only if you must admin style facility IMHO

Stored procedures and error handling

I have 10 stored procedures.
For example -
stored procedure fetches the rows from table A
then stored procedure runs and then third...
How can I do error handling in this.. for example I have to check with if first stored procedure executed successfully run second else throw error. If first executed successfully run second stored procedure if second runs successfully run third otherwise throw error.
ALTER PROCEDURE [dbo].[MASTER_PROCEDURE] AS
EXEC QRY_STEP3
EXEC QRY_STEP_3_1_1
EXEC OQRY_STEP_3_1_1
I would add logic to each of your subsidiary stored procedures to determine whether they have succeeded or not. eg test for existence of the temporary table. Then use a return value to indicate success of the proc. Typically this would be 0 for succes and non-zero for failure.
You would then call the procs from your master proc like this
DECLARE #ReturnValue INT
EXEC #ReturnValue = QRY_STEP1
IF(#ReturnValue = 0)
BEGIN
EXEC #ReturnValue = QRY_STEP2
END
ELSE
BEGIN
--REPORT ERROR
END
Using this approach, your master proc doesnt need to know about the inner workings of each child proc, and your master proc code will be cleaner and more readable.
use ##error.. Can be done like this
ALTER PROCEDURE [dbo].[MASTER_PROCEDURE] AS
EXEC QRY_STEP3
IF ##error =0
begin
EXEC QRY_STEP_3_1_1
else
begin
print "error in proc name"
return 1
End
if (##error=0)
Begin
EXEC OQRY_STEP_3_1_1
Else
print "error in proc name"
return 1
End
END
END
First to do this correctly you should use TRY CATCH blocks in the child packages. Those should return to the calling proc if there is an error. This way you can also return an error code to the calling proc if results are unexpected, such as a temp table with zero records which is not an error but which might make the subsequent procs fail.
Next, why are you using child procs at all? Honestly this is something that is probably better done in one proc. You say for instance that you are creating temp tables in one proc that you use in subsequent procs. To do this you need global temp tables. The problem is that global temp tables are not specific to the orginal connection that called them and thus two people trying to do this simluatnaeously might have their data mixedup. Whereas if you use one proc and local temp tables that can't happen.