我有一个存储过程,它返回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'.


当前回答

如果你能够修改你的存储过程,你可以很容易地把所需的列定义作为一个参数,并使用一个自动创建的临时表:

CREATE PROCEDURE sp_GetDiffDataExample
      @columnsStatement NVARCHAR(MAX) -- required columns statement (e.g. "field1, field2")
AS
BEGIN
    DECLARE @query NVARCHAR(MAX)
    SET @query = N'SELECT ' + @columnsStatement + N' INTO ##TempTable FROM dbo.TestTable'
    EXEC sp_executeSql @query
    SELECT * FROM ##TempTable
    DROP TABLE ##TempTable
END

在这种情况下,您不需要手动创建临时表—它是自动创建的。希望这能有所帮助。

其他回答

我知道从sp执行和插入到临时表或表变量将是一个选项,但我不认为这是你的要求。根据您的要求,以下查询语句应该工作:

Declare @sql nvarchar(max)
Set @sql='SELECT   col1, col2 FROM OPENROWSET(''SQLNCLI'', ''Server=(local);uid=test;pwd=test'',
     ''EXEC MyStoredProc ''''param1'''', ''''param2'''''')'
 Exec(@sql)

如果你有可信连接,那么使用下面的查询语句:

Declare @sql nvarchar(max)
Set @sql='SELECT   col1, col2 FROM OPENROWSET(''SQLNCLI'', ''Server=(local);Trusted_Connection=yes;'',
     ''EXEC MyStoredProc ''''param1'''', ''''param2'''''')'
 Exec(@sql)

如果运行上面的语句出错,那么只需运行下面的语句:

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

我希望这将帮助那些将面临类似问题的人。如果有人想尝试temp表或表变量,应该如下所示,但在这种情况下,你应该知道sp返回多少列,然后你应该在temp表或表变量中创建这么多列:

--for table variable 
Declare @t table(col1 col1Type, col2 col2Type)
insert into @t exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM @t

--for temp table
create table #t(col1 col1Type, col2 col2Type)
insert into #t exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM #t

这适用于我:(即我只需要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语句不能嵌套。”)

CREATE TABLE #Result
(
  ID int,  Name varchar(500), Revenue money
)
INSERT #Result EXEC RevenueByAdvertiser '1/1/10', '2/1/10'
SELECT * FROM #Result ORDER BY Name
DROP TABLE #Result

来源: http://stevesmithblog.com/blog/select-from-a-stored-procedure/

创建一个动态视图,并从中获得结果.......

CREATE PROCEDURE dbo.usp_userwise_columns_value
(
    @userid BIGINT
)
AS 
BEGIN
        DECLARE @maincmd NVARCHAR(max);
        DECLARE @columnlist NVARCHAR(max);
        DECLARE @columnname VARCHAR(150);
        DECLARE @nickname VARCHAR(50);

        SET @maincmd = '';
        SET @columnname = '';
        SET @columnlist = '';
        SET @nickname = '';

        DECLARE CUR_COLUMNLIST CURSOR FAST_FORWARD
        FOR
            SELECT columnname , nickname
            FROM dbo.v_userwise_columns 
            WHERE userid = @userid

        OPEN CUR_COLUMNLIST
        IF @@ERROR <> 0
            BEGIN
                ROLLBACK
                RETURN
            END   

        FETCH NEXT FROM CUR_COLUMNLIST
        INTO @columnname, @nickname

        WHILE @@FETCH_STATUS = 0
            BEGIN
                SET @columnlist = @columnlist + @columnname + ','

                FETCH NEXT FROM CUR_COLUMNLIST
                INTO @columnname, @nickname
            END
        CLOSE CUR_COLUMNLIST
        DEALLOCATE CUR_COLUMNLIST  

        IF NOT EXISTS (SELECT * FROM sys.views WHERE name = 'v_userwise_columns_value')
            BEGIN
                SET @maincmd = 'CREATE VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), @userid) + ') as userid , ' 
                            + CHAR(39) + @nickname + CHAR(39) + ' as nickname, ' 
                            + @columnlist + ' compcode FROM dbo.SJOTran '
            END
        ELSE
            BEGIN
                SET @maincmd = 'ALTER VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), @userid) + ') as userid , ' 
                            + CHAR(39) + @nickname + CHAR(39) + ' as nickname, ' 
                            + @columnlist + ' compcode FROM dbo.SJOTran '
            END

        --PRINT @maincmd
        EXECUTE sp_executesql @maincmd
END

-----------------------------------------------
SELECT * FROM dbo.v_userwise_columns_value

对于任何使用SQL 2012或更高版本的用户,我都能够使用非动态且每次都有相同列输出的存储过程来实现这一点。

总体思想是构建动态查询来创建、插入、选择和删除临时表,并在生成所有临时表后执行。首先从存储过程中检索列名和类型,动态生成临时表。

注意:如果您愿意/能够更新SP或更改配置并使用OPENROWSET,那么有更好、更通用的解决方案,它们只需要更少的代码行。如果没有其他方法,请使用下面的方法。

DECLARE @spName VARCHAR(MAX) = 'MyStoredProc'
DECLARE @tempTableName VARCHAR(MAX) = '#tempTable'

-- might need to update this if your param value is a string and you need to escape quotes
DECLARE @insertCommand VARCHAR(MAX) = 'INSERT INTO ' + @tempTableName + ' EXEC MyStoredProc @param=value'

DECLARE @createTableCommand VARCHAR(MAX)

-- update this to select the columns you want
DECLARE @selectCommand VARCHAR(MAX) = 'SELECT col1, col2 FROM ' + @tempTableName

DECLARE @dropCommand VARCHAR(MAX) = 'DROP TABLE ' + @tempTableName

-- Generate command to create temp table
SELECT @createTableCommand = 'CREATE TABLE ' + @tempTableName + ' (' +
    STUFF
    (
        (
            SELECT ', ' + CONCAT('[', name, ']', ' ', system_type_name)
            FROM sys.dm_exec_describe_first_result_set_for_object
            (
              OBJECT_ID(@spName), 
              NULL
            )
            FOR XML PATH('')
        )
        ,1
        ,1
        ,''
    ) + ')'

EXEC( @createTableCommand + ' '+ @insertCommand + ' ' + @selectCommand + ' ' + @dropCommand)