SQL - Dynamic SQL inside OpenRowSet then Inserted into Temp Table - sql-server

I am trying to utilize Dynamic SQL that will parse in the file directory into an OpenRowSet(#Database). Right now I have a hard coded directory for the Excel File. The end result will be me creating an application that will take the user's file and import into the SQL table. From there I'll be merging and Match/Not Matching (Which is working properly). This is the last piece of the puzzle. I don't know why the error message is looking for my file in "C:\WINDOWS\system32\ "
My current error messages are:
OLE DB provider "Microsoft.ACE.OLEDB.12.0" for linked server "(null)" returned message "The Microsoft Access database engine could not find the object 'C:\WINDOWS\system32\ C:\Users\GrimRieber\Desktop\isi test.xlsx'. Make sure the object exists and that you spell its name and the path name correctly. If 'C:\WINDOWS\system32\ C:\Users\GrimRieber\Desktop\isi test.xlsx' is not a local object, check your network connection or contact the server administrator.".
Msg 7303, Level 16, State 1, Line 1
Cannot initialize the data source object of OLE DB provider "Microsoft.ACE.OLEDB.12.0" for linked server "(null)".
Code:
declare #Database varchar(max)
select #Database = 'C:\Users\GrimRieber\Desktop\isi test.xlsx'
declare #sql varchar(Max)
select #sql = 'SELECT * FROM OPENROWSET(
''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0; Database= ' + #Database + '; HDR=YES; IMEX=1'',
''SELECT [Vendor],[VendorName],[Material],[MaterialName],[Supplier Stock Num],[01],[02],[03],[04],[05],[06],[07],[08],[09],[10],[11],[12],[Year]FROM [Data$]''
)'
IF OBJECT_ID('tempdb.dbo.#TempScorecardInventorySold', 'U') IS NOT NULL
DROP TABLE #TempScorecardInventorySold;
CREATE TABLE #TempScorecardInventorySold
(
[Vendor] [varchar](50) NULL,
[VendorName] [varchar](50) NULL,
[Material] [varchar](50) NULL,
[MaterialName] [varchar](50) NULL,
[Supplier Stock Num] [varchar](50) NULL,
[01] [nVarchar](50) NULL,
[02] [nVarchar](50) NULL,
[03] [nVarchar](50) NULL,
[04] [nVarchar](50) NULL,
[05] [nVarchar](50) NULL,
[06] [nVarchar](50) NULL,
[07] [nVarchar](50) NULL,
[08] [nVarchar](50) NULL,
[09] [nVarchar](50) NULL,
[10] [nVarchar](50) NULL,
[11] [nVarchar](50) NULL,
[12] [nVarchar](50) NULL,
[Year] [Int] Null
) ON [PRIMARY];
INSERT INTO [dbo].#TempScorecardInventorySold ([Vendor],[VendorName],[Material],[MaterialName],[Supplier Stock Num],[01],[02],[03],[04],[05],[06],[07],[08],[09],[10],[11],[12],[Year])
EXECUTE(#sql)

While I cannot recreate your file path issue (be sure to carefully check if file exists at the path and file extension), the OPENROWSET() in that setup should define fields in the SELECT clause at beginning. The last argument should be unquoted, pointing to worksheet range:
select #sql = 'SELECT [Vendor],[VendorName],[Material],[MaterialName],[Supplier Stock Num],
[01],[02],[03],[04],[05],[06],
[07],[08],[09],[10],[11],[12],[Year]
FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0; Database= ' + #Database + '; HDR=YES; IMEX=1'', [Data$])';
Alternatively, consider OPENDATASOURCE:
select #sql = 'SELECT [Vendor],[VendorName],[Material],[MaterialName],[Supplier Stock Num],
[01],[02],[03],[04],[05],[06],
[07],[08],[09],[10],[11],[12],[Year]
FROM OPENDATASOURCE(''Microsoft.ACE.OLEDB.12.0'',
''Data Source=' + #Database + ';Extended Properties=Excel 12.0'')...Data$'
Or even a Driver version (fields can be in either of two SELECT clauses)
select #sql = 'SELECT [Vendor],[VendorName],[Material],[MaterialName],[Supplier Stock Num],
[01],[02],[03],[04],[05],[06],
[07],[08],[09],[10],[11],[12],[Year]
FROM OPENROWSET(''MSDASQL'',
''Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};
DBQ=' + #Database + ''', ''SELECT * FROM [DATA$]'');

Related

SELECT sp_WhoIsActive into Table

I'm trying to script sp_WhoIsActive into a table. The goal is feed the table with an Agent Job every 10 seconds.
I followed this guide and I tried to feed the table this way:
--Log activity into table.
DECLARE #destination_table VARCHAR(4000) =
'[Monitoring].[dbo].[WhoIsActive] '
EXEC sp_WhoIsActive
#get_plans = 1,
#get_transaction_info = 1,
#destination_table = #destination_table;
But as result I receive the error:
Warning: The join order has been enforced because a local join hint is used.
Msg 50000, Level 16, State 1, Procedure sp_WhoIsActive, Line 1111 [Batch Start Line 0]
Destination table not properly formatted.
On Google I found many guides talking about a solution that could help me execute a Stored Procedure into a temp table and from there I could create a table:
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;',
'EXEC sp_WhoIsActive')
SELECT * FROM #MyTempTable
But this process too is failing with error:
Msg 11526, Level 16, State 1, Procedure sys.sp_describe_first_result_set, Line 1 [Batch Start Line 12]
The metadata could not be determined because statement 'INSERT #sessions
(
recursion,
session_id,
request_id' in procedure 'sp_WhoIsActive' uses a temp table.
I tried following Kendra Little blog but that too is not working.
In the end I scripted out the table manually:
CREATE TABLE [dbo].[WhoIsActive](
[dd_hh_mm_ss_mss] [nvarchar](50) NOT NULL,
[session_id] [tinyint] NOT NULL,
[sql_text] [nvarchar](max) NOT NULL,
[sql_command] [nvarchar](400) NOT NULL,
[login_name] [nvarchar](50) NOT NULL,
[wait_info] [nvarchar](50) NOT NULL,
[tran_log_writes] [nvarchar](50) NOT NULL,
[CPU] [smallint] NOT NULL,
[tempdb_allocations] [smallint] NOT NULL,
[tempdb_current] [smallint] NOT NULL,
[blocking_session_id] [nvarchar](50) NOT NULL,
[reads] [int] NOT NULL,
[writes] [float] NOT NULL,
[physical_reads] [tinyint] NOT NULL,
[query_plan] [nvarchar](50) NOT NULL,
[used_memory] [tinyint] NOT NULL,
[status] [nvarchar](50) NOT NULL,
[tran_start_time] [datetime2](7) NOT NULL,
[implicit_tran] [nvarchar](50) NOT NULL,
[open_tran_count] [tinyint] NOT NULL,
[percent_complete] [nvarchar](50) NOT NULL,
[host_name] [nvarchar](50) NOT NULL,
[database_name] [nvarchar](50) NOT NULL,
[program_name] [nvarchar](100) NOT NULL,
[start_time] [datetime2](7) NOT NULL,
[login_tine] [datetime2](7) NOT NULL,
[request_id] [tinyint] NOT NULL,
[collection_time] [datetime2](7) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
But that too is failing and I cannot feed the table with a job.
sp_WhoIsActive is so popular, I cannot believe I'm the only one trying to insert the results into a table.
You need to create a table suitable to use with the output of the procedure, the schema of which can vary depending on the options you use.
SP_WhoIsActive will actually give you the create script, so to capture the default options just do
declare #definition varchar(max)
exec sp_WhoIsActive #return_schema = 1, #schema = #definition output
print #definition
This returns the appropriate T-SQL:
CREATE TABLE < table_name > (
[dd hh:mm:ss.mss] VARCHAR(8000) NULL
,[session_id] SMALLINT NOT NULL
,[sql_text] XML NULL
,[login_name] NVARCHAR(128) NOT NULL
,[wait_info] NVARCHAR(4000) NULL
,[CPU] VARCHAR(30) NULL
,[tempdb_allocations] VARCHAR(30) NULL
,[tempdb_current] VARCHAR(30) NULL
,[blocking_session_id] SMALLINT NULL
,[reads] VARCHAR(30) NULL
,[writes] VARCHAR(30) NULL
,[physical_reads] VARCHAR(30) NULL
,[used_memory] VARCHAR(30) NULL
,[status] VARCHAR(30) NOT NULL
,[open_tran_count] VARCHAR(30) NULL
,[percent_complete] VARCHAR(30) NULL
,[host_name] NVARCHAR(128) NULL
,[database_name] NVARCHAR(128) NULL
,[program_name] NVARCHAR(128) NULL
,[start_time] DATETIME NOT NULL
,[login_time] DATETIME NULL
,[request_id] INT NULL
,[collection_time] DATETIME NOT NULL
)
Edit and run with the required target table name, then you can run sp_whoisactive with the destination table option
exec sp_WhoIsActive #destination_table='Monitoring.dbo.WhoIsActive'
See the docs

SQL pivot with unknown number of columns - not a valid identifier and showing minimum only

I am having an issue with a dynamic query to pivot on an unknown number of columns in MS SQL server 2014. I've based my query on the article SQL Server 2005 Pivot on Unknown Number of Columns and other similar articles. However, I am having two issues that I can't decipher.
When executing the dynamic SQL, I get this error:
The name 'SELECT Code, LastName, FirstName, [03-30-2021],[06-30-2021],[08-00-2021],[10-30-2021],[12-30-2021],[17-30-2021],[18-30-2021],[19-30-2021],[20-30-2021],[21-30-2021],[22-30-2021],[24-30-2021],[25-30-2021],[26-30-2021],[29-30-2021] FROM (
SELECT i.Code, aa.LastName, aa.FirstName, FORMAT(StartDate, 'dd-mm-yyyy') AS StartDate, FORMAT(s.SignOut-s.SignIn, 'hh:mm') AS AttendanceTime
FROM ActualSession AS a INNER JOIN ActualAttendee aa ON( a.id = aa.ActualSessionId)
INNER JOIN Attendee att ON (att.Id = aa.AttendeeId)
LEFT JOIN SignIn s ON (a.Id = s.ActualSessionId) LEFT JOIN Identification i ON (i.AttendeeId = aa.AttendeeId
AND i.Id' is not a valid identifier.
However, if I copy and paste the query into a separate window in SQL Server Management Studio, it runs... I can't see what is wrong, except that the error message is only returning the first xxx characters of the query... Any suggestions as to what I have done wrong would be appreciated. Is it the joins, or the way I've selected columns?
BUT... that takes me to the second issue. When I do run the copied query text separately, it works but it is showing the same values (time in hours/minutes) for all users, rather than the user-specific values (though users who did not sign in are correctly coming up NULL). The pivot seems to need a min/max/other aggregate, but where it should be keeping it to each user (according to what I can see, by some SQL voodoo), it's not... The column names also appear to be coming up in US date format, even though I've specified the format as Australian (dd-mm-yyyy). If anyone knows how to correct these issues, that would also be appreciated.
Code LastName FirstName 03-30-2021 06-30-2021 08-00-2021 10-30-2021
abc123 Simpson Homer 01:07 01:15 NULL 01:01
abc456 Griffen Peter 01:07 01:15 NULL 01:01
abc789 Flintsone Fred 01:07 01:15 NULL 01:01
xyz123 Heffernan Doug 01:07 01:15 NULL 01:01
xyz456 Gergich Jerry NULL NULL NULL NULL
xyz789 Kramden Ralph 01:07 01:15 NULL 01:01
The full query I am running is:
#Query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT DISTINCT '],[' + FORMAT(StartDate, 'dd-mm-yyyy') FROM ActualSession
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,2,'') + ']'
SET #Query = 'SELECT Code, LastName, FirstName, ' + #cols + ' FROM (
SELECT i.Code, aa.LastName, aa.FirstName, FORMAT(StartDate, ''dd-mm-yyyy'') AS StartDate, FORMAT(s.SignOut-s.SignIn, ''hh:mm'') AS AttendanceTime
FROM ActualSession AS a INNER JOIN ActualAttendee aa ON( a.id = aa.ActualSessionId)
INNER JOIN Attendee att ON (att.Id = aa.AttendeeId)
LEFT JOIN SignIn s ON (a.Id = s.ActualSessionId) LEFT JOIN Identification i ON (i.AttendeeId = aa.AttendeeId
AND i.IdentificationTypeId = (SELECT Id FROM IdentificationType WHERE [Name] = ''Student Code''))
) x PIVOT ( max(AttendanceTime)
FOR StartDate in (' + #cols + ') ) p '
PRINT #Query --for debugging
execute #Query
Relevant Table definitions are:
CREATE TABLE [dbo].[ActualSession](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[EventId] [bigint] NOT NULL,
[EventName] [nvarchar](50) NOT NULL,
[EventSessionId] [bigint] NOT NULL,
[StartDate] [datetime] NOT NULL,
[EndDate] [datetime] NOT NULL,
[Active] [bit] NULL,
[SignInRequired] [bit] NULL,
[SignOutRequired] [bit] NULL,
[SignInAllowed] [bit] NULL,
[SignOutAllowed] [bit] NULL,
[EarlySignInAllowed] [bit] NULL,
[EarlySignOutAllowed] [bit] NULL,
[LateSignInAllowed] [bit] NULL,
[LateSignOutAllowed] [bit] NULL,
[ExpiredIdAllowed] [bit] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Attendee](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[PreferredName] [nvarchar](50) NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[SignIn](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[EventId] [bigint] NOT NULL,
[ActualSessionId] [bigint] NOT NULL,
[AttendeeId] [bigint] NOT NULL,
[SignIn] [datetime] NOT NULL,
[SignOut] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Identification](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[AttendeeId] [bigint] NOT NULL,
[IdentificationTypeId] [bigint] NOT NULL,
[Code] [nvarchar](50) NOT NULL,
[ExpiryDate] [date] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[IdentificationType](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[RevHex] [bit] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[ActualAttendee](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[EventId] [bigint] NOT NULL,
[ActualSessionId] [bigint] NOT NULL,
[AttendeeId] [bigint] NOT NULL,
[FirstName] [nvarchar](50) NULL,
[LastName] [nvarchar](50) NULL,
[PreferredName] [nvarchar](50) NULL,
[GroupId] [bigint] NULL,
[GroupName] [nvarchar](50) NULL,
[GroupTypeId] [bigint] NULL,
[GroupTypeName] [nvarchar](50) NULL,
[GroupTypeAD] [bit] NULL,
[GroupTypeADName] [nvarchar](200) NULL,
[GroupTypeDB] [bit] NULL,
[GroupTypeDBQuery] [nvarchar](1000) NULL,
[GroupTypeManual] [bit] NULL
) ON [PRIMARY]
GO
You have two issues here:
Your main issue: when you call execute you need to enclose #Query in parenthesis (). I suggest, though, that you use sp_executesql, because this enables you to pass through parameters if necessary
Your second problem: don't try to quote the column names yourself, use QUOTENAME
DECLARE #Query AS nvarchar(MAX), #cols nvarchar(MAX);
SELECT #cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME(FORMAT(StartDate, 'dd-mm-yyyy'))
FROM ActualSession
FOR XML PATH(''), TYPE
).value('text()[1]', 'NVARCHAR(MAX)')
, 1, LEN(','), '');
SET #Query = '
SELECT
Code,
LastName,
FirstName,
' + #cols + '
FROM (
SELECT
i.Code,
aa.LastName,
aa.FirstName,
FORMAT(StartDate, ''dd-mm-yyyy'') AS StartDate,
FORMAT(s.SignOut-s.SignIn, ''hh:mm'') AS AttendanceTime
FROM ActualSession AS a
INNER JOIN ActualAttendee aa ON (a.id = aa.ActualSessionId)
INNER JOIN Attendee att ON (att.Id = aa.AttendeeId)
LEFT JOIN SignIn s ON (a.Id = s.ActualSessionId)
LEFT JOIN Identification i ON (i.AttendeeId = aa.AttendeeId
AND i.IdentificationTypeId = (SELECT Id FROM IdentificationType WHERE [Name] = ''Student Code''))
) x
PIVOT ( max(AttendanceTime)
FOR StartDate in (
' + #cols + '
) ) p;
';
PRINT #Query --for debugging
EXEC sp_executesql #Query;
I believe the error you are receiving is related to the usage of execute #Query instead of execute (#Query). Looking at the Microsoft doc on EXECUTE, executing a string variable requires open and close parens:
Execute a character string
{ EXEC | EXECUTE }
( { #string_variable | [ N ]'tsql_string' } [ + ...n ] )
[ AS { LOGIN | USER } = ' name ' ]
[;]
Once I added the parens, I was able to get what I believe is an equivalent statement to yours to execute as expected with some sample data I created.
As for your date format, I believe your date format string has an issue. I believe your current date format string of dd-mm-yyyy is meant to be dd-MM-yyyy. Per the Custom date and time format strings Microsoft doc (referenced by Microsoft doc on FORMAT):
"mm" The minute, from 00 through 59.
"MM" The month, from 01 through 12.

SQL Server 2016 SP1 - Is this a bug?

I just installed SQL Server 2016 and SP1 and run this TSQL Script
CREATE TABLE [dbo].TEST(
[id] [INT] IDENTITY(1,1) NOT NULL,
[lat] [DECIMAL](9, 6) NULL,
[lng] [DECIMAL](9, 6) NULL,
[Location] AS ([geography]::STGeomFromText(((('POINT ('+[lng])+' ')+[lat])+')',(4326)))
PERSISTED
)
The table is created fine.
I then run the following (there is no records in the table)
Select * from TEST
This returns
Msg 8114, Level 16, State 5, Line 8
Error converting data type varchar to numeric.
It is related to the Location field.
Is this a bug in SQL Server 2016? I would not expect this behavior.
The following does not cause any issues
CREATE TABLE [dbo].TEST2(
[id] [INT] IDENTITY(1,1) NOT NULL,
[lat] [DECIMAL](9, 6) NULL,
[lng] [DECIMAL](9, 6) NULL,
[Location] AS [lng] PERSISTED
)
select * from TEST2
Change
[Location] AS ([geography]::STGeomFromText(((('POINT (' + [lng]) + ' ') + [lat]) + ')', (4326)))
to
[Location] AS ([geography]::STGeomFromText(((('POINT (' + CAST([lng] AS varchar)) + ' ') + CAST([lat] AS varchar)) + ')', (4326)))
The difference in these two are the usage of the CAST function like so:
CAST([lng] AS varchar)
CAST([lat] AS varchar)
Simply an issue of [lng] and [lat] being decimals concatenated to a varchar string. The reason for it not being a problem until you actually execute the query even with no data in the table is due to the [Location] field not being formed until the select query is executed.
Not a bug necessarily but something that SQL Management Studio should probably parse for prior to executing the CREATE TABLE query.
You need to CAST the lng and lat values to varchar so the + operator is treated as concatenation instead of addition. Example without the extraneous parenthesis:
CREATE TABLE [dbo].TEST(
[id] [INT] IDENTITY(1,1) NOT NULL,
[lat] [DECIMAL](9, 6) NULL,
[lng] [DECIMAL](9, 6) NULL,
[Location] AS [geography]::STGeomFromText('POINT ('+ CAST([lng] AS varchar(10)) + ' ' + CAST([lat] AS varchar(10)) + ')', 4326)
PERSISTED
);

Temporary tables in a WHILE LOOP

I have a question concerning a dynamic creation of temporary tables.
I need a while loop in which temporary tables are created in every cycle differing in their name using the variable #i. So I specified the variables #CreateTableVerbGES and #ShowTableVerbGES with the code as strings and want to execute them in the loop.
Here is my code:
DECLARE #i int = 1
DECLARE #CreateTableVerbGES nvarchar(max)
DECLARE #ShowTableVerbGES nvarchar (max)
WHILE #i<4
BEGIN
SET #CreateTableVerbGES = 'CREATE TABLE #UnterBaugruppe'+CAST((#i) as nvarchar(max))+' ([IDTBG] [int] identity(1,1),[IDGES] [int] NULL,[Baugruppe1] [nvarchar](max) NULL,[IDBG1] [int] NULL,[Bauteil1] [int] NULL,[Baugruppe2] [nvarchar](max) NULL,[IDBG2] [int] NULL,[Bauteil2] [int] NULL,[Baugruppe3] [nvarchar](max) NULL,[IDBG3] [int] NULL,[Bauteil3] [int] NULL)'
SET #ShowTableVerbGES = 'SELECT * FROM #UnterBaugruppe'+CAST((#i) as nvarchar(max))
EXEC (#CreateTableVerbGES)
EXEC (#ShowTableVerbGES)
SET #i += 1
END
The problem is that it seems like the first cycle of the loop is running, because the first table #UnterBaugruppe1 is shown. But for the next two cycles there is a warning that the tables #UnterBaugruppe2 and #UnterBaugruppe3 are invalid and don't exist.
Does anybody have an idea why it isn't working?
I really need the tables in a loop because I want to insert different information in the loop as well dependent on #i.
I just concatenated the dynamic sql of two variable into single using new line character and executed and it perfectly worked for me!
DECLARE #i int = 1
DECLARE #CreateTableVerbGES nvarchar(max)
DECLARE #ShowTableVerbGES nvarchar (max)
WHILE #i<4
BEGIN
SET #CreateTableVerbGES = 'CREATE TABLE #UnterBaugruppe'+CAST((#i) as nvarchar(max))+' ([IDTBG] [int] identity(1,1),[IDGES] [int] NULL,[Baugruppe1] [nvarchar](max) NULL,[IDBG1] [int] NULL,[Bauteil1] [int] NULL,[Baugruppe2] [nvarchar](max) NULL,[IDBG2] [int] NULL,[Bauteil2] [int] NULL,[Baugruppe3] [nvarchar](max) NULL,[IDBG3] [int] NULL,[Bauteil3] [int] NULL)'
+ CHar(10) + Char(13) + 'SELECT * FROM #UnterBaugruppe'+CAST((#i) as nvarchar(max)) + CHar(10) + Char(13)
Print #CreateTableVerbGES
EXEC (#CreateTableVerbGES)
--EXEC (#ShowTableVerbGES)
SET #i += 1
END

TRY/CATCH does not work on SQL Server Agent error?

I use sp_start_job to start a job.
The job (test2) has only one step:
select getdate()
waitfor delay '00:00:10'
The TRY/CATCH code:
begin try
EXEC msdb.dbo.sp_start_job #job_name = 'test2'
end try
begin catch
print 'error'
end catch
First run of the code:
Job 'test2' started successfully.
Second run of the code (within 10 seconds):
Msg 22022, Level 16, State 1, Line 0SQLServerAgent Error: Request to run job test2 (from User sa) refused because the job is already running from a request by User sa.
Why does TRY/CATCH not work in this scenario?
UPDATE:
I should added at first that I am working on a sql server 2005 which has linked servers (sql server 2000). I was trying to write a proc on the sql server 2005 server to see a job on all the linked servers. If the job is not running, run it. Initially, I used try - catch and hoped to catch any error when run the already running job but failed (this thread).
I finally used following code: (it won't compile, you need to substitute some variables, just gives an idea)
CREATE TABLE [dbo].[#jobInfo](
[job_id] [uniqueidentifier] NULL,
[originating_server] [nvarchar](30) ,
[name] [nvarchar](128) ,
[enabled] [tinyint] NULL,
[description] [nvarchar](512) ,
[start_step_id] [int] NULL,
[category] [nvarchar](128) ,
[owner] [nvarchar](128) ,
[notify_level_eventlog] [int] NULL,
[notify_level_email] [int] NULL,
[notify_level_netsend] [int] NULL,
[notify_level_page] [int] NULL,
[notify_email_operator] [nvarchar](128) ,
[notify_netsend_operator] [nvarchar](128) ,
[notify_page_operator] [nvarchar](128) ,
[delete_level] [int] NULL,
[date_created] [datetime] NULL,
[date_modified] [datetime] NULL,
[version_number] [int] NULL,
[last_run_date] [int] NOT NULL,
[last_run_time] [int] NOT NULL,
[last_run_outcome] [int] NOT NULL,
[next_run_date] [int] NOT NULL,
[next_run_time] [int] NOT NULL,
[next_run_schedule_id] [int] NOT NULL,
[current_execution_status] [int] NOT NULL,
[current_execution_step] [nvarchar](128) ,
[current_retry_attempt] [int] NOT NULL,
[has_step] [int] NULL,
[has_schedule] [int] NULL,
[has_target] [int] NULL,
[type] [int] NOT NULL
)
SET #sql =
'INSERT INTO #jobInfo
SELECT * FROM OPENQUERY( [' + #srvName + '],''set fmtonly off exec msdb.dbo.sp_help_job'')'
EXEC(#sql)
IF EXISTS (select * from #jobInfo WHERE [name] = #jobName AND current_execution_status IN (4,5)) -- 4: idle, 5: suspended
BEGIN
SET #sql = 'EXEC [' + #srvName + '].msdb.dbo.sp_start_job #job_name = ''' + #jobName + ''''
--print #sql
EXEC (#sql)
INSERT INTO #result (srvName ,status ) VALUES (#srvName, 'Job started.')
END ELSE BEGIN
INSERT INTO #result (srvName ,status ) VALUES (#srvName, 'Job is running already. No action taken.')
END
Not all errors can be caught by TRY/CATCH. In this case, sp_start_job actually calls external procedures, and these are outside the bounds of SQL Server's error handling. Or at least that's the story that they're sticking to:
http://connect.microsoft.com/SQLServer/feedback/details/362112/sp-start-job-error-handling
Also note that this is still a problem in SQL Server 2012 SP1 CU3. Please vote and comment if you want this bug fixed.
A tedious but viable workaround, which requires certain permissions and in this case assumes the job owner is sa:
DECLARE #x TABLE
(
a VARBINARY(32),b INT,c INT,d INT,e INT,f INT,g INT,h INT,i NVARCHAR(64),
Running BIT, -- the only important column
k INT,l INT,m INT
);
DECLARE #job_id UNIQUEIDENTIFIER;
SELECT #job_id = job_id FROM msdb.dbo.sysjobs WHERE name = N'test2';
INSERT #x EXEC master.dbo.xp_sqlagent_enum_jobs 1, N'sa', #job_id;
IF EXISTS (SELECT 1 FROM #x WHERE Running = 0)
BEGIN
EXEC msdb.dbo.sp_start_job #job_name = N'test2';
END
ELSE
BEGIN
PRINT 'error';
END
Even better might be:
DECLARE #job_id UNIQUEIDENTIFIER, #d DATETIME;
SELECT #job_id = job_id FROM msdb.dbo.sysjobs WHERE name = N'test2';
SELECT #d = stop_execution_date
FROM msdb.dbo.sysjobactivity WHERE job_id = #job_id;
IF #d IS NOT NULL
BEGIN
EXEC msdb.dbo.sp_start_job #job_name = N'test2';
END
ELSE
BEGIN
PRINT 'error';
END
In either case, it is still possible that the job has started between the check for its status and the call to start it, so this doesn't eliminate errors from sp_start_job altogether, but it makes them far less likely to occur.
You can use alerts to execute SQL Agent jobs. And you can use alerts' options to configure required response.
(this will help you to totally avoid error 22022 but you will have additional records in the errorlog)
To make an evergreen solution, we should also consider a job that has never run before.
This my be useful in a scenario where the SQL server is rebuilt and the jobs recreated.
To capture that scenario look at the last run requested date:
DECLARE #jobEnd DATETIME, #jobRun DATETIME
SELECT #jobEnd = sja.stop_execution_date , #jobRun = sja.run_requested_date
FROM msdb.dbo.sysjobactivity AS sja
INNER JOIN msdb.dbo.sysjobs AS sj
ON sja.job_id = sj.job_id
WHERE sj.name = 'PhoneListSyncMember'
IF (#jobEnd IS NOT NULL AND #jobRun IS NOT NULL)
OR (#jobEnd IS NULL AND #jobRun IS NULL) -- job is New and never run before
EXEC #iRet =msdb.dbo.sp_start_job #job_name='PhoneListSyncMember'
Here is how you can capture Job trigger failures.
Declare #returnstatus int
Exec #returnstatus msdb.dbo.sp_start_job 'Jobname'
if(#returnstaus = 1)
Print 'Success'
else
Print 'Failure'

Resources