我有一个存储过程,它返回80列和300行。我要写一个select函数,它能得到2个这样的列。类似的

SELECT col1, col2 FROM EXEC MyStoredProc 'param1', 'param2'

当我使用上面的语法时,我得到了错误:

“无效的列名”。

我知道最简单的解决方案是更改存储过程,但我没有编写它,也不能更改它。

有什么办法能让我如愿以偿吗?

I could make a temp table to put the results in, but because there are 80 columns so I would need to make an 80 column temp table just to get 2 columns. I wanted to avoid tracking down all the columns that are returned. I tried using WITH SprocResults AS .... as suggested by Mark, but I got 2 errors Incorrect syntax near the keyword 'EXEC'.Incorrect syntax near ')'. I tried declaring a table variable and I got the following error Insert Error: Column name or number of supplied values does not match table definition If I try SELECT * FROM EXEC MyStoredProc 'param1', 'param2' I get the error : Incorrect syntax near the keyword 'exec'.


当前回答

正如问题中提到的,在执行存储过程之前很难定义80列的临时表。

另一种方法是根据存储过程结果集填充表。

SELECT * INTO #temp FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;'
                                   ,'EXEC MyStoredProc')

如果您得到任何错误,您需要通过执行以下查询来启用特定的分布式查询。

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

要使用两个参数执行sp_configure以更改配置选项或运行RECONFIGURE语句,必须授予ALTER SETTINGS服务器级权限

现在可以从生成的表中选择特定的列

SELECT col1, col2
FROM #temp

其他回答

这里有一个链接,指向一个非常好的文档,它解释了解决您的问题的所有不同方法(尽管其中许多方法不能使用,因为您不能修改现有的存储过程)。

如何在存储过程之间共享数据

Gulzar的答案是可行的(在上面的链接中有文档),但是编写起来会很麻烦(您需要在@tablevar(col1,…)语句中指定所有80个列名。在将来,如果一个列被添加到模式中,或者输出被改变,它将需要在您的代码中更新,否则它将出错。

这适用于我:(即我只需要sp_help_job返回的30+的2列)

SELECT name, current_execution_status 
FROM OPENQUERY (MYSERVER, 
  'EXEC msdb.dbo.sp_help_job @job_name = ''My Job'', @job_aspect = ''JOB''');  

在这之前,我需要运行这个:

sp_serveroption 'MYSERVER', 'DATA ACCESS', TRUE;

....更新sys. xml文件。服务器表。(例如,在OPENQUERY中使用自引用默认情况下是禁用的。)

对于我的简单需求,我没有遇到Lance的出色链接的OPENQUERY部分中描述的任何问题。

Rossini,如果你需要动态地设置这些输入参数,那么OPENQUERY的使用就变得更加精细了:

DECLARE @innerSql varchar(1000);
DECLARE @outerSql varchar(1000);

-- Set up the original stored proc definition.
SET @innerSql = 
'EXEC msdb.dbo.sp_help_job @job_name = '''+@param1+''', @job_aspect = N'''+@param2+'''' ;

-- Handle quotes.
SET @innerSql = REPLACE(@innerSql, '''', '''''');

-- Set up the OPENQUERY definition.
SET @outerSql = 
'SELECT name, current_execution_status 
FROM OPENQUERY (MYSERVER, ''' + @innerSql + ''');';

-- Execute.
EXEC (@outerSql);

我不确定使用sp_serveroption来更新现有的sys. exe之间的差异(如果有的话)。服务器直接自引用,而不是使用sp_addlinkedserver(如Lance的链接中所述)来创建副本/别名。

注1: 我更喜欢OPENQUERY而不是OPENROWSET,因为OPENQUERY不需要进程内的连接字符串定义。

注2: 说了这么多:通常我会使用INSERT…是的,这是额外的10分钟打字,但如果我能帮助它,我宁愿不跳: (a)引用中引用,以及 (b)系统表,和/或狡猾的自引用链接服务器设置(即,对于这些,我需要向我们全能的dba辩护:)

但是在这个例子中,我不能使用INSERT…EXEC构造,因为sp_help_job已经使用了一个。(“INSERT EXEC语句不能嵌套。”)

如果你只需要这样做一次,最简单的方法是:

在导入和导出向导中导出到excel,然后将此excel导入到表中。

知道这为什么如此困难也许会有所帮助。 存储过程可以只返回文本(print 'text'),也可以返回多个表,或者根本不返回表。

所以像SELECT * FROM (exec sp_tables) Table1这样的东西将不起作用

如果您这样做是为了手动验证数据,您可以使用LINQPad。

在LinqPad中创建一个到数据库的连接,然后创建类似于下面的c#语句:

DataTable table = MyStoredProc (param1, param2).Tables[0];
(from row in table.AsEnumerable()
 select new
 {
  Col1 = row.Field<string>("col1"),
  Col2 = row.Field<string>("col2"),
 }).Dump();

参考http://www.global-webnet.net/blogengine/post/2008/09/10/LINQPAD-Using-Stored-Procedures-Accessing-a-DataSet.aspx