从存储过程的结果集中select列
我有一个存储过程,返回80列和300行。 我想写一个select,获得这些列中的2。 就像是
SELECT col1, col2 FROM EXEC MyStoredProc 'param1', 'param2'
当我使用上面的语法,我得到的错误:
“无效的列名称”。
我知道最简单的解决scheme将是更改存储过程,但我没有写,我不能改变它。
有什么办法可以做我想要的吗?
-
我可以做一个临时表来放置结果,但是因为有80列,所以我需要制作一个80列的临时表才能得到2列。 我想避免跟踪所有返回的列。
-
我试图使用
WITH SprocResults AS ....
如马克build议,但我得到了2个错误关键字“EXEC”附近的语法不正确。
)附近语法不正确。 -
我试图声明一个表variables,我得到了以下错误
插入错误:提供的值的列名或数量与表定义不匹配
-
如果我尝试
SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
我得到的错误:关键字'exec'附近的语法不正确。
你能分解查询吗? 将存储的proc结果插入表variables或临时表中。 然后,从表variables中select2列。
Declare @tablevar table(col1,.. insert into @tablevar(col1,..) exec MyStoredProc 'param1', 'param2' SELECT col1, col2 FROM @tablevar
这里有一个很好的文档链接,解释所有不同的方法来解决你的问题(虽然很多不能使用,因为你不能修改现有的存储过程。)
如何在存储过程之间共享数据
Gulzar的答案是可行的(在上面的链接中有logging),但是写起来会很麻烦(你需要在@tablevar(col1,…)语句中指定所有80个列名。如果一个列被添加到模式或者输出被改变了,它将需要在你的代码中被更新,否则会出错。
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/
这适用于我:(即我只需要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.servers
表。 (即在OPENQUERY中使用自引用似乎被默认禁用。)
对于我这个简单的要求,我遇到了Lance优秀链接的OPENQUERY部分所描述的问题。
罗西尼,如果你需要dynamic设置这些input参数,那么使用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.servers
自引用与使用sp_addlinkedserver
(如Lance的链接中所述)创build重复/别名之间的差异(如果有的话)。
注1:我比OPENROWSET更喜欢OPENQUERY,因为OPENQUERY不需要proc中的连接string定义。
注2:说完这一切:通常我只是使用INSERT … EXEC :)是的,这是额外的打字10分钟,但如果我能帮助它,我宁愿不跳过:
(a)报价单内的报价单,以及
(b)系统表,和/或偷偷摸摸的自引用链接服务器设置(即这些,我需要恳求我的情况,我们的全能DBA 🙂
但是在这个例子中,我不能使用INSERT … EXEC结构,因为sp_help_job
已经在使用了。 (“一个INSERT EXEC语句不能被嵌套。”)
知道为什么如此困难可能会有帮助。 存储过程只能返回文本(打印“文本”),或者可能返回多个表,或者根本不返回任何表。
所以像SELECT * FROM (exec sp_tables) Table1
这样的东西是行不通的
(假设SQL Server)
在T-SQL中使用存储过程的结果的唯一方法是使用INSERT INTO ... EXEC
语法。 这使您可以select插入临时表或表variables,并从那里select所需的数据。
为了达到这个目的,首先创build一个#test_table
如下所示:
create table #test_table( col1 int, col2 int, . . . col80 int )
现在执行程序并把值放在#test_table
:
insert into #test_table EXEC MyStoredProc 'param1', 'param2'
现在您从#test_table
获取值:
select col1,col2....,col80 from #test_table
如果您可以修改存储过程,则可以轻松地将所需的列定义作为参数,并使用自动创build的临时表:
CREATE PROCEDURE sp_GetDiffDataExample @columnsStatement NVARCHAR(MAX) -- required columns statement (eg "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
在这种情况下,您不需要手动创build临时表 – 它会自动创build。 希望这可以帮助。
快速'@Column_Name'
将是添加一个新的参数'@Column_Name'
并让调用函数定义要检索的列名称。 在你的sproc的返回部分,你会有if / else语句,只返回指定的列,或者是空的 – 返回全部。
CREATE PROCEDURE [dbo].[MySproc] @Column_Name AS VARCHAR(50) AS BEGIN IF (@Column_Name = 'ColumnName1') BEGIN SELECT @ColumnItem1 as 'ColumnName1' END ELSE BEGIN SELECT @ColumnItem1 as 'ColumnName1', @ColumnItem2 as 'ColumnName2', @ColumnItem3 as 'ColumnName3' END END
如果你这样做的手动validation的数据,你可以用LINQPad做到这一点。
在LinqPad中创build到数据库的连接,然后创build与以下类似的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();
对于SQL Server,我发现这工作正常:
创build一个临时表(或永久表,实际上并不重要),并对存储过程做一个插入语句。 SP的结果集应该匹配表中的列,否则会出错。
这是一个例子:
DECLARE @temp TABLE (firstname NVARCHAR(30), lastname nvarchar(50)); INSERT INTO @temp EXEC dbo.GetPersonName @param1,@param2; -- assumption is that dbo.GetPersonName returns a table with firstname / lastname columns SELECT * FROM @temp;
而已!
尝试这个
use mydatabase create procedure sp_onetwothree as select 1 as '1', 2 as '2', 3 as '3' go SELECT a.[1], a.[2] FROM OPENROWSET('SQLOLEDB','myserver';'sa';'mysapass', 'exec mydatabase.dbo.sp_onetwothree') AS a GO
正如问题中提到的那样,在执行存储过程之前很难定义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
来更改configuration选项或运行RECONFIGURE
语句,您必须被授予ALTER SETTINGS
服务器级权限
现在,您可以从生成的表格中select特定的列
SELECT col1, col2 FROM #temp
我会剪切和粘贴原始的SP,并删除除了你想要的2之外的所有列。 要么。 我会把结果集,映射到一个适当的业务对象,然后LINQ出两列。
如果你只需要这样一次,最简单的方法就是:
在导入和导出向导中导出为excel,然后将该excel导入到表中。