为什么请求 sp_delete_job 的“估计执行计划”会抛出错误?

Han*_*non 5 sql-server sql-server-agent

我正在尝试查看以下 T-SQL 语句的估计执行计划:

EXEC msdb.dbo.sp_delete_job @job_id = '3A015189-F4EB-439B-9CA0-27AFB74719D8'
    , @originating_server = '(LOCAL)'
    , @delete_history = 0
    , @delete_unused_schedule = 0;
Run Code Online (Sandbox Code Playgroud)

当我这样做时,SQL Server 抛出以下错误:

消息 213,级别 16,状态 1,过程 sp_delete_all_msx_jobs,第 25 行

列名或提供的值数量与表定义不匹配。

的定义sp_delete_all_msx_jobs包含以下相关行,从第 25 行开始(为了可读性而包装):

INSERT INTO #temp_jobs_to_delete
SELECT sjv.job_id, 
    CASE sjs.server_id WHEN 0 THEN 1 ELSE 0 END,
    sjv.owner_sid
FROM msdb.dbo.sysjobs_view sjv
    LEFT OUTER JOIN msdb.dbo.sysjobservers sjs 
        ON (sjv.job_id = sjs.job_id)
WHERE (ISNULL(sjs.server_id, 0) = 0)
    AND (sjv.originating_server = @msx_server)
Run Code Online (Sandbox Code Playgroud)

该存储过程的第 14 行显示了#temp_jobs_to_deleteis的定义:

CREATE TABLE #temp_jobs_to_delete 
(
    job_id UNIQUEIDENTIFIER NOT NULL
    , job_is_cached INT NOT NULL
    , owner_sid VARBINARY(85) NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎有效。为什么“显示估计的执行计划”返回错误?

我可以在 SQL Server 2008R2 和 2012 上运行它;两者都返回错误。我正在使用 SSMS 2012。

Aar*_*and 6

因为微软还没有学会创建唯一的对象名称。如果您查看 的定义sp_delete_job,您会在第 115 行附近发现它,这是一个略有不同的 版本#temp_jobs_to_delete

CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED,
                                   job_is_cached INT NOT NULL)
Run Code Online (Sandbox Code Playgroud)

现在,考虑到 SQL Server 在为一组嵌套过程调用生成估计的执行计划时可能会采用与仅运行代码略有不同的路径。它可能会执行某种对象级集合,识别使用的相同 #table 并假定它必须遵循相同的模式(即使逻辑上两个 #temp 表永远不会交叉路径)。

现在,当您尝试删除一个作业并且该作业的属性导致sp_delete_all_msx_jobs被调用时,此代码实际上可能会中断,我怀疑在您的情况下这在实际执行中没有发生(也没有发生在我的情况下)。我没有精力设置 MSX 进行评估。但是请记住在解析过程中发生的一些其他事情,这些事情在实际执行中不可能成为问题:

IF (1 = 1)
  CREATE TABLE #x(i INT);
ELSE
  CREATE TABLE #x(d DATETIME);
Run Code Online (Sandbox Code Playgroud)

不合逻辑的解析错误:

消息 2714,级别 16,状态 1
数据库中已经有一个名为“#x”的对象。


Geo*_*son 4

看起来有两个相互冲突的定义#temp_jobs_to_delete

一份来自sp_delete_job

-- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references,
-- so it cannot be declared as a local table.
CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED,
                                   job_is_cached INT NOT NULL)
Run Code Online (Sandbox Code Playgroud)

另一个来自sp_delete_all_msx_jobs

-- Delete all the jobs that originated from the MSX
-- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references
CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL,
    job_is_cached INT NOT NULL, owner_sid VARBINARY(85) NOT NULL)
Run Code Online (Sandbox Code Playgroud)

我可以使用以下简化脚本重现该错误:

CREATE PROC #test1 AS
BEGIN
    CREATE TABLE #temp_jobs_to_delete (
        job_id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED,
        job_is_cached INT NOT NULL
    )

    IF (GETDATE() < '1/1/2015') -- Always false
        EXEC #test2

    DROP TABLE #temp_jobs_to_delete
END
GO

CREATE PROC #test2 AS
BEGIN
    CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL,
        job_is_cached INT NOT NULL, owner_sid VARBINARY(85) NOT NULL)

    INSERT INTO #temp_jobs_to_delete
    SELECT NEWID() AS job_id, 0 AS job_is_cached, 0x1234 AS owner_sid
END
GO

-- This statement runs successfully, but fails to compile when viewing an estimated plan
EXEC #test1
GO
Run Code Online (Sandbox Code Playgroud)

我认为正在发生的事情是 SQL Server 使用第一次创建的定义#temp_jobs_to_delete来尝试编译计划的其余部分。事实上,我看不到在调用之前sp_delete_job会下降的任何地方(或其调用的过程)。我可能会遗漏一些东西,但如果您使用参数值调用该过程,似乎也会收到运行时错误 ,从而导致必须调用该过程。#temp_jobs_to_deletesp_delete_all_msx_jobs@originating_serversp_delete_all_msx_jobs

-- Do the delete (for all jobs originating from the specific server)
IF (@originating_server IS NOT NULL)
BEGIN
EXECUTE msdb.dbo.sp_delete_all_msx_jobs @msx_server = @originating_server
Run Code Online (Sandbox Code Playgroud)

请注意,该@originating_server参数具有以下注释,并且联机丛书指定它仅供内部使用。

@originating_server sysname = NULL, -- Reserved (used by SQLAgent)
Run Code Online (Sandbox Code Playgroud)