笔记 - 探究 SQL Server 中以 sp_ 前置词命名之预存进程的运行行为

笔记 - 探究 SQL Server 中以 sp_ 前置词命名之预存进程的运行行为


大家应该都知道,SQL Server 有内建许多系统预存进程,许多管理和参考活动,都可以利用系统预存进程加以执行。在 SQL Server 2005 中,系统预存进程实例是保存于内部隐藏的资源 (Resource) 数据库,逻辑上会出现在每个系统自订和使用者自订数据库的 sys 结构描述中,且会有 sp_ 前置词。本文的目的不是要介绍系统预存进程或其用法,而是要探讨 sp_ 前置词所建立的预存进程,有着什么样的特殊行为,以及为什么 Microsoft 强烈建议不要以 sp_ 前置词建立自订的预存进程…其中的一个重要原因是,SQL Server 使用这个前置词来标明系统预存进程,但其中是否有更具有说服力的说法呢?让我们往下看…


首先,根据前述的说法(摘录自线上手册),系统预存进程逻辑上会存在每一个数据库 (不管系统内建的或使用者自订的) 的 sys 结构描述中,例如执行底操作码可以分别查询 pubs、Northwind 数据库所有的数据表资讯:

EXEC sys.sp_tables;
GO

USE Northwind;
EXEC sys.sp_tables;
GO


执行结果如下:

sp_tables

可以看到 sp_tables 实际上在任一数据库都可以执行,这是系统预存进程具有的独特行为。


接下来,我们在 master 数据库底下,自行建立 sp_proc1 预存进程,一开始印出数据库名称以确定现行数据库环境,然后分别以动态跟静态方式查询 INFORMATION_SCHEMA.TABLES:

USE master;
GO

IF object_id('dbo.sp_proc1') IS NOT NULL
	DROP PROC dbo.sp_proc1;
GO

CREATE PROC dbo.sp_proc1
AS
PRINT 'master.dbo.sp_proc1 executing in ' + DB_NAME();

-- 动态查询
EXEC('SELECT TOP 3 TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = ''BASE TABLE'';')

-- 静态查询
SELECT TOP 3 TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';
GO



以这种方式建立的预存进程具有特殊行为,其特点为连线到不同数据库时,仍然可以执行 (不必以数据库限定名称),但有一个问题,运行行为变得难以捉摸,请看底下执行结果:

sp_proc1

动态 SQL 是采用现行数据库的相关环境,因此我们的预存进程中,EXEC 命令回传来自 Northwind 的数据表名称,但静态查询的部分似乎“认为”master 数据库才是它的执行环境 -- 它返回来自 master 的数据表名称(*注)。为了更进一步确定 sp_前置词的魔力,我们将 master 底下的 dbo.sp_proc1 透过 SSMS 按鼠标右键直接重新命名为 dbo.usp_proc1,然后分别在 Northwind、pubs、master 里叫用:

usp_proc1

仅在 master 数据库里才能顺利执行,Northwind、pubs 里都出现“找不到预存进程 'dbo.usp_proc1'。”的错误消息。


最后,我们透过 SSMS 按鼠标右键再度将 master 底下的 dbo.usp_proc1 重新命名回 dbo.sp_proc1,然后于 Northwind 数据库底下也建立一个 dbo.sp_proc1 的预存进程,命令码如下,可以看到执内联容除了第 2 行以及第 11 行,将数据库改为 Northwind 以外,其余完全相同 master 底下的 dbo.sp_proc1:

USE Northwind;
GO

IF object_id('dbo.sp_proc1') IS NOT NULL
	DROP PROC dbo.sp_proc1;
GO

CREATE PROC dbo.sp_proc1
AS
PRINT 'Northwind.dbo.sp_proc1 executing in ' + DB_NAME();

-- 动态查询
EXEC('SELECT TOP 3 TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = ''BASE TABLE'';')

-- 静态查询
SELECT TOP 3 TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';
GO



随后执行如下查询并观察结果:

sp_proc1_local

注意,我们先前的执行结果是 master.dbo.sp_proc1 内的静态查询会返回 master 的数据表名称,但现在于 Northwind 执行的结果却是回传 Northwind 所属的数据表名称,并且执行后的回传消息也明确指出确实是叫用 Northwind 本地端预存进程:

sp_proc1_local_message


本文一开始说明了 SQL Server 系统预存进程的独特行为,接着测试在 master 数据库底下以 sp_ 前置词命名自订的预存进程的运行方式以及问题,最后提醒你,假如在自有的数据底下建立的预存进程,与系统预存进程具有相同结构描述 (Schema) 与名称的话,会被调用的是自订版本的进程,以上种种理由看起来,我们可以理解何以 Microsoft 强烈建议要避免使用者定义预存进程使用 sp_ 前置词来命名,如果实际上你仍然需要这么做,那至少你应该知道会遭遇到哪些问题,并想办法避免才是。



*备注:
我在看 Inside Microsoft SQL Server 2005:T-SQL Programming 这本书的时候,其中有提到 sp_MS_marksystemobject 进程,可以把自订进程标记为系统型。但非常不建议在实际环境上这么做…因为此进程并没有正式文档说明,遇到问题时无法得到任何支持,并且在相容性上也有疑虑,所以放在备注栏当补充数据。

接续本文在 master 数据库底下,自行建立 sp_proc1 预存进程之后的环境,我们以下列命令将其标注为系统型预存进程:

USE master;
EXEC sys.sp_MS_marksystemobject 'dbo.sp_proc1';
GO



你会发现 master 底下的 dbo.sp_proc1 被归类到系统预存进程了,再度由 Northwind、pubs 叫用此进程时,进程中所有的陈述式都会采用现行数据库环境:

sp_proc1_mark sp_proc1_mark_message

这样的行为已经跟系统预存进程没什么两样了。至于要不要这样用各位看官或许可以在网络上用 "sp_MS_marksystemobject" 关键字搜寻一下,自行决定吧!



参考数据:
  • SQL Server 线上丛书 - 预存进程类型、系统预存进程、资源数据库
  • Inside Microsoft SQL Server 2005:T-SQL Programming (中文版)


原文:大专栏  笔记 - 探究 SQL Server 中以 sp_ 前置词命名之预存进程的运行行为


猜你喜欢

转载自www.cnblogs.com/petewell/p/11452548.html