|
Question : How to retrieve results from a stored procedure?
|
|
I am trying to retrieve the results from a stored procedure in code. (Currently running jobs from sp_help_job) I cannot use INSERT INTO... EXEC because the stored procedure itself uses an INSERT INTO... EXEC statement, and MS-SQL does not permit a nested INSERT INTO... EXEC. I tried to return the EXEC sp_xxx from a function as RETURNS TABLE, but it is not possible because if I use an "Inline Table-valued Function" the only permitted statement is SELECT, and if I use a "Multi-statement Table-valued Function" that permits EXEC statement, I have to state a table name and definition and return that table. Either retrieving results from a stored procedure, or having another way to get the currently running jobs, will be a good solution for me. I really need to done this, please help me. Thank you!
|
|
Answer : How to retrieve results from a stored procedure?
|
|
I got the following answer on : http://www.tek-tips.com/viewthread.cfm?SQID=687152&SPID=183&page=1 Sorry about the length; I did not write it... The answer:
In your case, you should write your own SP instead of using sp_help_job and sp_get_composite_job_info.
1. first create sp_my_help_job:
CREATE PROCEDURE sp_my_help_job -- Individual job parameters @job_id UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name @job_name sysname = NULL, -- If provided should NOT also provide job_id @job_aspect VARCHAR(9) = NULL, -- JOB, STEPS, SCEDULES, TARGETS or ALL -- Job set parameters @job_type VARCHAR(12) = NULL, -- LOCAL or MULTI-SERVER @owner_login_name sysname = NULL, @subsystem NVARCHAR(40) = NULL, @category_name sysname = NULL, @enabled TINYINT = NULL, @execution_status INT = NULL, -- 1 = Executing, 2 = Waiting For Thread, 3 = Between Retries, 4 = Idle, 5 = Suspended, 6 = [obsolete], 7 = PerformingCompletionActions @date_comparator CHAR(1) = NULL, -- >, < or = @date_created DATETIME = NULL, @date_last_modified DATETIME = NULL, @description NVARCHAR(512) = NULL -- We do a LIKE on this so it can include wildcards AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @job_id_as_char VARCHAR(36) DECLARE @res_valid_range NVARCHAR(200)
SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @job_aspect = LTRIM(RTRIM(@job_aspect)) SELECT @job_type = LTRIM(RTRIM(@job_type)) SELECT @subsystem = LTRIM(RTRIM(@subsystem)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @description = LTRIM(RTRIM(@description))
-- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF (@job_aspect = '') SELECT @job_aspect = NULL IF (@job_type = '') SELECT @job_type = NULL IF (@owner_login_name = N'') SELECT @owner_login_name = NULL IF (@subsystem = N'') SELECT @subsystem = NULL IF (@category_name = N'') SELECT @category_name = NULL IF (@description = N'') SELECT @description = NULL
IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END
SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
-- If the user provided a job name or id but no aspect, default to ALL IF ((@job_name IS NOT NULL) OR (@job_id IS NOT NULL)) AND (@job_aspect IS NULL) SELECT @job_aspect = 'ALL'
-- The caller must supply EITHER job name (or job id) and aspect OR one-or-more of the set -- parameters OR no parameters at all IF (((@job_name IS NOT NULL) OR (@job_id IS NOT NULL)) AND ((@job_aspect IS NULL) OR (@job_type IS NOT NULL) OR (@owner_login_name IS NOT NULL) OR (@subsystem IS NOT NULL) OR (@category_name IS NOT NULL) OR (@enabled IS NOT NULL) OR (@date_comparator IS NOT NULL) OR (@date_created IS NOT NULL) OR (@date_last_modified IS NOT NULL))) OR ((@job_name IS NULL) AND (@job_id IS NULL) AND (@job_aspect IS NOT NULL)) BEGIN RAISERROR(14280, -1, -1) RETURN(1) -- Failure END
IF (@job_id IS NOT NULL) BEGIN -- Individual job...
-- Check job aspect SELECT @job_aspect = UPPER(@job_aspect) IF (@job_aspect NOT IN ('JOB', 'STEPS', 'SCHEDULES', 'TARGETS', 'ALL')) BEGIN RAISERROR(14266, -1, -1, '@job_aspect', 'JOB, STEPS, SCHEDULES, TARGETS, ALL') RETURN(1) -- Failure END
-- Generate results set...
IF (@job_aspect IN ('JOB', 'ALL')) BEGIN IF (@job_aspect = 'ALL') BEGIN RAISERROR(14213, 0, 1) PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14213)) / 2) END EXECUTE sp_get_detail_job_info @job_id, @job_type, @owner_login_name, @subsystem, @category_id, @enabled, @execution_status, @date_comparator, @date_created, @date_last_modified, @description END END -- mjia detele batch of if begin ... end ELSE BEGIN -- Set of jobs...
-- Check job type IF (@job_type IS NOT NULL) BEGIN SELECT @job_type = UPPER(@job_type) IF (@job_type NOT IN ('LOCAL', 'MULTI-SERVER')) BEGIN RAISERROR(14266, -1, -1, '@job_type', 'LOCAL, MULTI-SERVER') RETURN(1) -- Failure END END
-- Check owner IF (@owner_login_name IS NOT NULL) BEGIN IF (SUSER_SID(@owner_login_name) IS NULL) BEGIN RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name) RETURN(1) -- Failure END END
-- Check subsystem IF (@subsystem IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_subsystem @subsystem IF (@retval <> 0) RETURN(1) -- Failure END
-- Check job category IF (@category_name IS NOT NULL) BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (name = @category_name) IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END END
-- Check enabled state IF (@enabled IS NOT NULL) AND (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, -1, -1, '@enabled', '0, 1') RETURN(1) -- Failure END
-- Check current execution status IF (@execution_status IS NOT NULL) BEGIN IF (@execution_status NOT IN (0, 1, 2, 3, 4, 5, 7)) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14204) RAISERROR(14266, -1, -1, '@execution_status', @res_valid_range) RETURN(1) -- Failure END END
-- If a date comparator is supplied, we must have either a date-created or date-last-modified IF ((@date_comparator IS NOT NULL) AND (@date_created IS NOT NULL) AND (@date_last_modified IS NOT NULL)) OR ((@date_comparator IS NULL) AND ((@date_created IS NOT NULL) OR (@date_last_modified IS NOT NULL))) BEGIN RAISERROR(14282, -1, -1) RETURN(1) -- Failure END
-- Check dates / comparator IF (@date_comparator IS NOT NULL) AND (@date_comparator NOT IN ('=', '<', '>')) BEGIN RAISERROR(14266, -1, -1, '@date_comparator', '=, >, <') RETURN(1) -- Failure END IF (@date_created IS NOT NULL) AND ((@date_created < '1 Jan 1990 12:00:00am') OR (@date_created > '31 Dec 9999 11:59:59pm')) BEGIN RAISERROR(14266, -1, -1, '@date_created', '1/1/1990 12:00am .. 12/31/9999 11:59pm') RETURN(1) -- Failure END IF (@date_last_modified IS NOT NULL) AND ((@date_last_modified < '1 Jan 1990 12:00am') OR (@date_last_modified > 'Dec 31 9999 11:59:59pm')) BEGIN RAISERROR(14266, -1, -1, '@date_last_modified', '1/1/1990 12:00am .. 12/31/9999 11:59pm') RETURN(1) -- Failure END
-- Generate results set... EXECUTE sp_get_detail_job_info @job_id, @job_type, @owner_login_name, @subsystem, @category_id, @enabled, @execution_status, @date_comparator, @date_created, @date_last_modified, @description END
RETURN(0) -- Success END
2. then create SP sp_get_detail_job_info:
CREATE PROCEDURE sp_get_detail_job_info @job_id UNIQUEIDENTIFIER = NULL, @job_type VARCHAR(12) = NULL, -- LOCAL or MULTI-SERVER @owner_login_name sysname = NULL, @subsystem NVARCHAR(40) = NULL, @category_id INT = NULL, @enabled TINYINT = NULL, @execution_status INT = NULL, -- 0 = Not idle or suspended, 1 = Executing, 2 = Waiting For Thread, 3 = Between Retries, 4 = Idle, 5 = Suspended, [6 = WaitingForStepToFinish], 7 = PerformingCompletionActions @date_comparator CHAR(1) = NULL, -- >, < or = @date_created DATETIME = NULL, @date_last_modified DATETIME = NULL, @description NVARCHAR(512) = NULL -- We do a LIKE on this so it can include wildcards AS BEGIN DECLARE @is_sysadmin INT DECLARE @job_owner sysname
SET NOCOUNT ON
-- By 'composite' we mean a combination of sysjobs and xp_sqlagent_enum_jobs data. -- This proc should only ever be called by sp_help_job, so we don't verify the -- parameters (sp_help_job has already done this).
-- Step 1: Create intermediate work tables CREATE TABLE #job_execution_state (job_id UNIQUEIDENTIFIER NOT NULL, date_started INT NOT NULL, time_started INT NOT NULL, execution_job_status INT NOT NULL, execution_step_id INT NULL, execution_step_name sysname COLLATE database_default NULL, execution_retry_attempt INT NOT NULL, next_run_date INT NOT NULL, next_run_time INT NOT NULL, next_run_schedule_id INT NOT NULL) CREATE TABLE #filtered_jobs (job_id UNIQUEIDENTIFIER NOT NULL, date_created DATETIME NOT NULL, date_last_modified DATETIME NOT NULL, current_execution_status INT NULL, current_execution_step sysname COLLATE database_default NULL, current_retry_attempt INT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, last_run_outcome INT NOT NULL, next_run_date INT NULL, next_run_time INT NULL, next_run_schedule_id INT NULL, type INT NOT NULL) CREATE TABLE #xp_results (job_id UNIQUEIDENTIFIER NOT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, next_run_date INT NOT NULL, next_run_time INT NOT NULL, next_run_schedule_id INT NOT NULL, requested_to_run INT NOT NULL, -- BOOL request_source INT NOT NULL, request_source_id sysname COLLATE database_default NULL, running INT NOT NULL, -- BOOL current_step INT NOT NULL, current_retry_attempt INT NOT NULL, job_state INT NOT NULL)
-- Step 2: Capture job execution information (for local jobs only since that's all SQLServerAgent caches) SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) SELECT @job_owner = SUSER_SNAME()
IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater INSERT INTO #xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @job_owner, @job_id ELSE INSERT INTO #xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @job_owner
INSERT INTO #job_execution_state SELECT xpr.job_id, xpr.last_run_date, xpr.last_run_time, xpr.job_state, sjs.step_id, sjs.step_name, xpr.current_retry_attempt, xpr.next_run_date, xpr.next_run_time, xpr.next_run_schedule_id FROM #xp_results xpr LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON ((xpr.job_id = sjs.job_id) AND (xpr.current_step = sjs.step_id)), msdb.dbo.sysjobs_view sjv WHERE (sjv.job_id = xpr.job_id)
-- Step 3: Filter on everything but dates and job_type IF ((@subsystem IS NULL) AND (@owner_login_name IS NULL) AND (@enabled IS NULL) AND (@category_id IS NULL) AND (@execution_status IS NULL) AND (@description IS NULL) AND (@job_id IS NULL)) BEGIN -- Optimize for the frequently used case... INSERT INTO #filtered_jobs SELECT sjv.job_id, sjv.date_created, sjv.date_modified, ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in #job_execution_state (NOTE: 4 = STATE_IDLE) CASE ISNULL(jes.execution_step_id, 0) WHEN 0 THEN NULL -- Will be NULL if the job is non-local or is not in #job_execution_state ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')' END, jes.execution_retry_attempt, -- Will be NULL if the job is non-local or is not in #job_execution_state 0, -- last_run_date placeholder (we'll fix it up in step 3.3) 0, -- last_run_time placeholder (we'll fix it up in step 3.3) 5, -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job) jes.next_run_date, -- Will be NULL if the job is non-local or is not in #job_execution_state jes.next_run_time, -- Will be NULL if the job is non-local or is not in #job_execution_state jes.next_run_schedule_id, -- Will be NULL if the job is non-local or is not in #job_execution_state 0 -- type placeholder (we'll fix it up in step 3.4) FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN #job_execution_state jes ON (sjv.job_id = jes.job_id) END ELSE BEGIN INSERT INTO #filtered_jobs SELECT DISTINCT sjv.job_id, sjv.date_created, sjv.date_modified, ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in #job_execution_state (NOTE: 4 = STATE_IDLE) CASE ISNULL(jes.execution_step_id, 0) WHEN 0 THEN NULL -- Will be NULL if the job is non-local or is not in #job_execution_state ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')' END, jes.execution_retry_attempt, -- Will be NULL if the job is non-local or is not in #job_execution_state 0, -- last_run_date placeholder (we'll fix it up in step 3.3) 0, -- last_run_time placeholder (we'll fix it up in step 3.3) 5, -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job) jes.next_run_date, -- Will be NULL if the job is non-local or is not in #job_execution_state jes.next_run_time, -- Will be NULL if the job is non-local or is not in #job_execution_state jes.next_run_schedule_id, -- Will be NULL if the job is non-local or is not in #job_execution_state 0 -- type placeholder (we'll fix it up in step 3.4) FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN #job_execution_state jes ON (sjv.job_id = jes.job_id) LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON (sjv.job_id = sjs.job_id) WHERE ((@subsystem IS NULL) OR (sjs.subsystem = @subsystem)) AND ((@owner_login_name IS NULL) OR (sjv.owner_sid = SUSER_SID(@owner_login_name))) AND ((@enabled IS NULL) OR (sjv.enabled = @enabled)) AND ((@category_id IS NULL) OR (sjv.category_id = @category_id)) AND ((@execution_status IS NULL) OR ((@execution_status > 0) AND (jes.execution_job_status = @execution_status)) OR ((@execution_status = 0) AND (jes.execution_job_status <> 4) AND (jes.execution_job_status <> 5))) AND ((@description IS NULL) OR (sjv.description LIKE @description)) AND ((@job_id IS NULL) OR (sjv.job_id = @job_id)) END
-- Step 3.1: Change the execution status of non-local jobs from 'Idle' to 'Unknown' UPDATE #filtered_jobs SET current_execution_status = NULL WHERE (current_execution_status = 4) AND (job_id IN (SELECT job_id FROM msdb.dbo.sysjobservers WHERE (server_id <> 0)))
-- Step 3.2: Check that if the user asked to see idle jobs that we still have some. -- If we don't have any then the query should return no rows. IF (@execution_status = 4) AND (NOT EXISTS (SELECT * FROM #filtered_jobs WHERE (current_execution_status = 4))) BEGIN TRUNCATE TABLE #filtered_jobs END
-- Step 3.3: Populate the last run date/time/outcome [this is a little tricky since for -- multi-server jobs there are multiple last run details in sysjobservers, so -- we simply choose the most recent]. IF (EXISTS (SELECT * FROM msdb.dbo.systargetservers)) BEGIN UPDATE #filtered_jobs SET last_run_date = sjs.last_run_date, last_run_time = sjs.last_run_time, last_run_outcome = sjs.last_run_outcome FROM #filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (CONVERT(FLOAT, sjs.last_run_date) * 1000000) + sjs.last_run_time = (SELECT MAX((CONVERT(FLOAT, last_run_date) * 1000000) + last_run_time) FROM msdb.dbo.sysjobservers WHERE (job_id = sjs.job_id)) AND (fj.job_id = sjs.job_id) END ELSE BEGIN UPDATE #filtered_jobs SET last_run_date = sjs.last_run_date, last_run_time = sjs.last_run_time, last_run_outcome = sjs.last_run_outcome FROM #filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (fj.job_id = sjs.job_id) END
-- Step 3.4 : Set the type of the job to local (1) or multi-server (2) -- NOTE: If the job has no jobservers then it wil have a type of 0 meaning -- unknown. This is marginally inconsistent with the behaviour of -- defaulting the category of a new job to [Uncategorized (Local)], but -- prevents incompletely defined jobs from erroneously showing up as valid -- local jobs. UPDATE #filtered_jobs SET type = 1 -- LOCAL FROM #filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (fj.job_id = sjs.job_id) AND (server_id = 0) UPDATE #filtered_jobs SET type = 2 -- MULTI-SERVER FROM #filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (fj.job_id = sjs.job_id) AND (server_id <> 0)
-- Step 4: Filter on job_type IF (@job_type IS NOT NULL) BEGIN IF (UPPER(@job_type) = 'LOCAL') DELETE FROM #filtered_jobs WHERE (type <> 1) -- IE. Delete all the non-local jobs IF (UPPER(@job_type) = 'MULTI-SERVER') DELETE FROM #filtered_jobs WHERE (type <> 2) -- IE. Delete all the non-multi-server jobs END
-- Step 5: Filter on dates IF (@date_comparator IS NOT NULL) BEGIN IF (@date_created IS NOT NULL) BEGIN IF (@date_comparator = '=') DELETE FROM #filtered_jobs WHERE (date_created <> @date_created) IF (@date_comparator = '>') DELETE FROM #filtered_jobs WHERE (date_created <= @date_created) IF (@date_comparator = '<') DELETE FROM #filtered_jobs WHERE (date_created >= @date_created) END IF (@date_last_modified IS NOT NULL) BEGIN IF (@date_comparator = '=') DELETE FROM #filtered_jobs WHERE (date_last_modified <> @date_last_modified) IF (@date_comparator = '>') DELETE FROM #filtered_jobs WHERE (date_last_modified <= @date_last_modified) IF (@date_comparator = '<') DELETE FROM #filtered_jobs WHERE (date_last_modified >= @date_last_modified) END END
-- Return the result set (NOTE: No filtering occurs here) -- mjia modified select * into #pre_job_stat SELECT sjv.job_id, sjv.originating_server, sjv.name, sjv.enabled, sjv.description, sjv.start_step_id, category = ISNULL(sc.name, FORMATMESSAGE(14205)), owner = SUSER_SNAME(sjv.owner_sid), sjv.notify_level_eventlog, sjv.notify_level_email, sjv.notify_level_netsend, sjv.notify_level_page, notify_email_operator = ISNULL(so1.name, FORMATMESSAGE(14205)), notify_netsend_operator = ISNULL(so2.name, FORMATMESSAGE(14205)), notify_page_operator = ISNULL(so3.name, FORMATMESSAGE(14205)), sjv.delete_level, sjv.date_created, sjv.date_modified, sjv.version_number, fj.last_run_date, fj.last_run_time, fj.last_run_outcome, next_run_date = ISNULL(fj.next_run_date, 0), -- This column will be NULL if the job is non-local next_run_time = ISNULL(fj.next_run_time, 0), -- This column will be NULL if the job is non-local next_run_schedule_id = ISNULL(fj.next_run_schedule_id, 0), -- This column will be NULL if the job is non-local current_execution_status = ISNULL(fj.current_execution_status, 0), -- This column will be NULL if the job is non-local current_execution_step = ISNULL(fj.current_execution_step, N'0 ' + FORMATMESSAGE(14205)), -- This column will be NULL if the job is non-local current_retry_attempt = ISNULL(fj.current_retry_attempt, 0), -- This column will be NULL if the job is non-local has_step = (SELECT COUNT(*) FROM msdb.dbo.sysjobsteps sjst WHERE (sjst.job_id = sjv.job_id)), has_schedule = (SELECT COUNT(*) FROM msdb.dbo.sysjobschedules sjsch WHERE (sjsch.job_id = sjv.job_id)), has_target = (SELECT COUNT(*) FROM msdb.dbo.sysjobservers sjs WHERE (sjs.job_id = sjv.job_id)), type = fj.type FROM #filtered_jobs fj LEFT OUTER JOIN msdb.dbo.sysjobs_view sjv ON (fj.job_id = sjv.job_id) LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjv.notify_email_operator_id = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjv.notify_netsend_operator_id = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjv.notify_page_operator_id = so3.id) LEFT OUTER JOIN msdb.dbo.syscategories sc ON (sjv.category_id = sc.category_id) ORDER BY sjv.job_id -- mjia added SELECT job_id, last_run_date, last_run_time, next_run_date, next_run_time, next_run_schedule_id, ???? as requested_to_run, -- BOOL ???? as request_source, ???? as sysname, case when current_execution_status = 1 then 1 else 0 end as running, current_step, current_retry_attempt, job_state into #job_stat from #pre_job_stat -- codes that deal with #job_stat
-- Clean up DROP TABLE #job_execution_state DROP TABLE #filtered_jobs DROP TABLE #xp_results END
3. then run:
EXEC sp_my_help_job @job_name='xxx_job', @execution_status=1
|
|
|
|