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.
Related
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.
I've got a database that is replicated on two servers, a live server and a test server, so that whenever it's needed the 'test' database gets overwritten by the 'live' database (so that I can reset everything if I've made a mess.)
I want an Stored Procedure in the 'test' database, that will only run in the 'test' database, but to do this I need to have it in the 'live' database as well, so that it can be copied over when 'test' is overwritten.
The procedure starts:
if ##SERVERNAME<>'TEST'
begin
raiserror ('NOT ON TEST! This SP must only be run on TEST.',16,1)
return
end
So that if it runs in live, it immediately exits.
Unfortunately the "Live" database server uses an older version of SQL, and doesn't seem to understand the lead/lag/over statements in the script, and refuses to create the procedure because of these "Incorrect syntax" errors.
The SP definitely works in the test server.
Is there a way to disregard the error messages when creating a stored procedure?
I've found a prior question that explained how to make a stored procedure with the same name, but I need the stored procedure to contain the script that the server thinks is incorrect.
The only way to not get the stored-procedure validated when created, is to run a dynamic-sql query within.
Example:
CREATE PROCEDURE dbo.YourStoredProcedure AS
BEGIN
IF ##SERVERNAME<>'TEST'
BEGIN
RAISERROR ('NOT ON TEST! This SP must only be run on TEST.',16,1)
RETURN
END
DECLARE #SQL NVARCHAR = N'
SELECT rowOne
, rowTwo
, valueOne
, LEAD(valueOne) OVER (PARTITION BY rowOne ORDER BY rowTwo DESC) AS preValue
FROM dbo.YourTable
'
EXEC(#SQL)
END
Notes:
On the long term, try to find a better strategy than DB replication for different systems. Check Continuous Deployment
Make sure to check concatenated dynamic-sql for potential issues (sql injection). Check QUOTENAME()
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.
I'm running a SQL Server 2008 64 bit Developer Edition with Service Pack 1 installed.
I have a SQL Server Agent Job. Within this job I want to get the job_id of my own job.
On MSDN (http://msdn.microsoft.com/en-us/library/ms175575(v=SQL.100).aspx) you can find a description of using tokens in job steps. Wow, great, that's what I'm looking for!! Just use (JOBID).
Since SQL Server 2005 SP1 you have to use macro like $(ESCAPE_NONE(JOBID)). No problem.
But if you try the example:
DECLARE #name NVARCHAR(128)
select #name = name from msdb.dbo.sysjobs where job_id = $(ESCAPE_SQUOTE(JOBID))
PRINT #name
you get:
Incorrect syntax near 'ESCAPE_SQUOTE'. (Microsoft SQL Server, Error: 102)
Ok, now from the scratch:
PRINT N'$(ESCAPE_SQUOTE(JOBID))'
results in 0xE33FE637C10B3C49A6E958BB3EF06959 but the job_id is
37E63FE3-0BC1-493C-A6E9-58BB3EF06959
The "N'" I think makes an implicit conversion to NVARCHAR of the (JOBID)...
Ok, I think I have to care about the datatype of (JOBID). In the book "SQL Server 2008 Administration" on page 168/169 there's also an example of using (JOBID):
declare #jobid binary(16)
SELECT #jobid =Convert(Uniqueidentifier,$(ESCAPE_NONE(JOBID)))
results in:
Incorrect syntax near '('. (Microsoft SQL Server, Error: 102)
I'm totally confused now. Could please someone help me with a good advice or solution. Every kind of help is appreciated.
Best regards
Helmut
We had trouble with this recently and did not go the route you found in MSDN. Instead, we recovered the jobid from dbo.sysjobs by name directly (the opposite of your example) and then used that within the job to check execution status (exiting out of long running while loop if job state had changed).
declare #jobid uniqueidentifier
SELECT #jobid = job_id from msdb.dbo.sysjobs where name = '[blah]'
thanks for your answers. The problem is that I tried to parse the statement in job step. Then I got this error. While running the job there's no problem.
My very best solution now is:
declare #JobID uniqueidentifier
SELECT #JobID = $(ESCAPE_NONE(JOBID));
PRINT 'My JobID is ' + Convert(char(255), #JobID)
Now you handle with #JobID, but as far as I know until now you have to convert always to char(255).
Thanks to user state_dba on MSDN.
Just forget what parser is saying - variable resolution is done at runtime. Parser does not know about that.
This may sound obvious, but I get the error you quoted from your first sample, if I run it in a query window, but it runs perfectly well when I paste that script into a job step.
You can only use these tokens within job steps. And, given that we're not expecting any quotes in the jobid token, I'd use ESCAPE_NONE whenever you reference it.
For those that need an alternative method to get your own job ID without macros, for example, from within a stored procedure that a job step calls. I found the following here
DECLARE #SQL NVARCHAR(72),
#jobID UNIQUEIDENTIFIER,
#jobName SYSNAME
IF SUBSTRING(APP_NAME(),1,8) = 'SQLAgent'
BEGIN
SET #SQL = 'SET #guid = CAST(' + SUBSTRING(APP_NAME(), 30, 34) + ' AS UNIQUEIDENTIFIER)'
EXEC sp_executesql #SQL, N'#guid UNIQUEIDENTIFIER OUT', #guid = #jobID OUT
SELECT #jobName = name
FROM msdb..sysjobs
WHERE job_id = #jobID
END
I'm adapting a large number of SQL Server 2005 scripts to merge two of our databases together. The scripts are run from a .cmd file which calls sqlcmd to run the scripts in order. However, I'm experiencing one or two issues where the scripts are failing.
I'd like a quick way to get a look at the state of some of the scripts where they go wrong - check variable values, the results of some queries, stuff like that.
If I was having this problem with a .NET assembly, I'd augment the code with Debug.Assert or set breakpoints where I knew failures were going to occur, which would pause program execution and allow me to check out variable values.
I was wondering, is there an equivalent in SQL Server 2005?
I've never managed to make the integrated debugging work well with SQL Server - I usually resort to "printf" debugging, using either PRINT or RAISERROR statements. RAISERROR can do some basic argument formatting, to spit the values out to the messages window. E.g. if you have a parameter #Val1, of type int, you can do:
RAISERROR('Val1 = %i',10,1,#Val1) WITH NOWAIT
(the WITH NOWAIT option causes the message to appear immediately, rather than the usual SQL behaviour of buffering messages/outputs)
This will work:
-- Assert procedure equivalent to other languages.
-- raiserror() will cause sql execution to stop and throw execep in C# code that is running this statement.
-- Usage:
-- declare #shouldBeTrue bit
-- set #shouldBeTrue = case when 1=0 then 1 else 0 end
-- exec _AT3Assert #shouldBeTrue, 'failed'
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = '_AT3Assert' AND ROUTINE_SCHEMA = 'dbo' AND ROUTINE_TYPE = 'PROCEDURE')
EXEC ('DROP PROCEDURE dbo._AT3Assert')
GO
create procedure dbo._AT3Assert
#shouldBeTrue bit,
#errorMsg nvarchar (max)
AS
SET NOCOUNT ON;
if #shouldBeTrue is null or #shouldBeTrue <> 1
begin
raiserror (#errorMsg, -- Message text.
11, -- Severity.
1 -- State.
);
end
GO
I use batch files and check the error code like this:-
SQLCMD.EXE -b -l 30 -E -S <SERVER> -i "<SQLFILE>.sql">>"%LOG_FILE%"2>&1
IF ERRORLEVEL 1 (
ECHO. Failed.
) ELSE (
ECHO. Succeeded.
)