假设你在 tempdb 中有一个存储过程:
USE tempdb;
GO
CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
SET NOCOUNT ON;
SELECT foo = 1, bar = 'tooth';
END
GO
您可以通过一种相当复杂的方式来确定存储过程将输出的元数据。有几个注意事项,包括该过程只能输出一个结果集,如果不能精确确定,将对数据类型进行最佳猜测。它需要使用OPENQUERY 和一个将'DATA ACCESS' 属性设置为true 的环回链接服务器。您可以检查 sys.servers 以查看您是否已经有一个有效的服务器,但我们只需手动创建一个名为 loopback 的服务器:
EXEC master..sp_addlinkedserver
@server = 'loopback',
@srvproduct = '',
@provider = 'SQLNCLI',
@datasrc = @@SERVERNAME;
EXEC master..sp_serveroption
@server = 'loopback',
@optname = 'DATA ACCESS',
@optvalue = 'TRUE';
现在您可以将其作为链接服务器进行查询,您可以将任何查询(包括存储过程调用)的结果用作常规SELECT。所以你可以这样做(注意数据库前缀是很重要,否则你会得到错误11529和2812):
SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
如果我们可以执行SELECT *,我们也可以执行SELECT * INTO:
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
一旦#tmp 表存在,我们可以通过以下方式确定元数据(假设 SQL Server 2005 或更高版本):
SELECT c.name, [type] = t.name, c.max_length, c.[precision], c.scale
FROM sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#tmp');
(如果您使用的是 SQL Server 2000,您可以使用 syscolumns 执行类似的操作,但我没有方便的 2000 实例来验证等效查询。)
结果:
name type max_length precision scale
--------- ------- ---------- --------- -----
foo int 4 10 0
bar varchar 5 0 0
在德纳利,这会容易得多、容易得多。同样,第一个结果集仍然存在限制,但您不必设置链接服务器并跳过所有这些环节。你可以说:
DECLARE @sql NVARCHAR(MAX) = N'EXEC tempdb.dbo.my_procedure;';
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set(@sql, NULL, 1);
结果:
name system_type_name
--------- ----------------
foo int
bar varchar(5)
在 Denali 之前,我建议卷起袖子自己弄清楚数据类型会更容易。不仅因为执行上述步骤很乏味,还因为您比引擎更可能做出正确(或至少更准确)的猜测,因为引擎做出的数据类型猜测将基于运行时输出,没有任何可能值域的外部知识。这一因素在 Denali 中也将保持不变,因此不要认为新的元数据发现功能是万能的,它们只会使上述内容变得不那么乏味。
哦,对于 OPENQUERY 的其他一些潜在问题,请在此处查看 Erland Sommarskog 的文章:
http://www.sommarskog.se/share_data.html#OPENQUERY