[我才意识到我以前回答过这个问题]
对存储过程执行此操作比对视图或表执行此操作要复杂得多。问题之一是存储过程可以有多个不同的代码路径,具体取决于输入参数,甚至是您无法控制的东西,例如服务器状态、一天中的时间等。例如,您希望看到的输出这个存储过程?如果有多个结果集而不考虑条件怎么办?
CREATE PROCEDURE dbo.foo
@bar INT
AS
BEGIN
SET NOCOUNT ON;
IF @bar = 1
SELECT a, b, c FROM dbo.blat;
ELSE
SELECT d, e, f, g, h FROM dbo.splunge;
END
GO
如果您的存储过程没有代码路径,并且您确信您将始终看到相同的结果集(并且可以提前确定如果存储过程具有非可选参数应提供哪些值),让我们举一个简单的示例:
CREATE PROCEDURE dbo.bar
AS
BEGIN
SET NOCOUNT ON;
SELECT a = 'a', b = 1, c = GETDATE();
END
GO
FMTONLY
一种方法是做这样的事情:
SET FMTONLY ON;
GO
EXEC dbo.bar;
这将为您提供一个空结果集,您的客户端应用程序可以查看该结果集的属性以确定列名和数据类型。
现在,有很多问题SET FMTONLY ON;
我不会在这里讨论,但至少应该注意这个命令已被弃用- 有充分的理由。完成后还要小心SET FMTONLY OFF;
,否则您会想知道为什么成功创建存储过程但无法执行它。不,我不会警告你,因为它只是发生在我身上。诚实。:-)
公开查询
通过创建一个环回链接服务器,您可以使用诸如OPENQUERY
执行存储过程之类的工具,但返回您可以检查的可组合结果集(好吧,请接受它作为一个非常松散的定义)。首先创建一个环回服务器(这假设一个名为 的本地实例FOO
):
USE master;
GO
EXEC sp_addlinkedserver @server = N'.\FOO', @srvproduct=N'SQL Server'
GO
EXEC sp_serveroption @server=N'.\FOO', @optname=N'data access',
@optvalue=N'true';
现在我们可以采用上面的过程并将其输入到这样的查询中:
SELECT * INTO #t
FROM OPENQUERY([.\FOO], 'EXEC dbname.dbo.bar;')
WHERE 1 = 0;
SELECT c.name, t.name
FROM tempdb.sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#t');
这会忽略别名类型(以前称为用户定义的数据类型),并且还可能为定义为的列显示两行,例如sysname
. 但从上面它产生:
name name
---- --------
b int
c datetime
a varchar
显然,这里还有更多工作要做 -varchar
不显示长度,您必须获得其他类型的精度/比例,例如datetime2
,time
和decimal
. 但这是一个开始。
SQL Server 2012
SQL Server 2012 中有一些新功能使元数据发现变得更加容易。对于上述过程,我们可以执行以下操作:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(
OBJECT_ID('dbo.bar'),
NULL
);
除其他外,这实际上为我们提供了精度和规模并解析了别名类型。对于上述过程,这会产生:
name system_type_name
---- ----------------
a varchar(1)
b int
c datetime
视觉上没有太大区别,但是当您开始以不同的精度和规模进入所有不同的数据类型时,您会欣赏此功能为您所做的额外工作。
缺点:在 SQL Server 2012 中,至少这些函数仅适用于第一个结果集(正如函数名称所暗示的那样)。