2011-08-01 67 views
1

我在MSSQL 2008中有一个存储过程,在这里我创建了一个临时表,然后我执行了几个插入临时表。 我如何选择存储过程之外的临时表的所有列?我是说,我有这样的:从SP和临时表中选择值

CREATE PROCEDURE [dbo].[LIST_CLIENTS] 

    CREATE TABLE #CLIENT(
     --Varchar And Numeric Values goes here 
    ) 

    /*Several Select's and Insert's against the Temporary Table*/ 

    SELECT * FROM #CLIENT 

END 

在另一个查询我这样做:

sp_configure 'Show Advanced Options', 1 
GO 
RECONFIGURE 
GO 

sp_configure 'Ad Hoc Distributed Queries', 1 
GO 
RECONFIGURE 
GO 

    SELECT * 
    INTO #CLIENT 
    FROM OPENROWSET 
    ('SQLOLEDB','Server=(local);Uid=Cnx;pwd=Cnx;database=r8;Trusted_Connection=yes; 
    Integrated Security=SSPI', 
    'EXEC dbo.LIST_CLIENTS ''20110602'', NULL, NULL, NULL, NULL, NULL') 

,但我得到这个错误:

Msg 208, Level 16, State 1, Procedure LIST_CLIENTS, Line 43 
Invalid object name '#CLIENT'. 

我试着全局临时表和它不起作用。 我知道这是临时表的范围,但是,如何获得SP范围之外的表?

在此先感谢

+0

能否请您标记与SQL Server的版本,您正在使用的问题?另外,如果您使用的是可信连接,那么为什么您要通过uid和pwd? –

+0

你知道全局临时表是用##声明的吗? –

+0

@AaronBertrand Tagged –

回答

1

我认为这里有更深层次的东西。

一个想法是在存储过程中使用表变量而不是#temp表(我必须假设您使用的是SQL Server 2005+,但事先说明这一点很好)。并使用OPENQUERY而不是OPENROWSET。这对我来说工作得很好:

USE tempdb; 
GO 
CREATE PROCEDURE dbo.proc_x 
AS 
BEGIN 
    SET NOCOUNT ON; 

    DECLARE @x TABLE(id INT); 
    INSERT @x VALUES(1),(2); 
    SELECT * FROM @x; 
END 
GO 

SELECT * 
    INTO #client 
    FROM OPENQUERY 
    (
    [loopback linked server name], 
    'EXEC tempdb.dbo.proc_x' 
) AS y; 

SELECT * FROM #client; 

DROP TABLE #client; 

DROP PROCEDURE dbo.proc_x; 

另一个想法是,也许是错误的,而无需使用SELECT INTO甚至发生。存储过程是否在任何动态SQL中引用#CLIENT表,例如?当你自己打电话或者只是说SELECT * FROM OPENROWSET而不是SELECT INTO时,它会起作用吗?很显然,如果你正在使用动态SQL中的#temp表格,你将会遇到与动态SQL中的@table变量一样的范围问题。

至少,请将外部#temp表的名称命名为#CLIENT以外的其他名称,以避免混淆 - 那么至少没有人需要猜测哪个#temp表未被正确引用。

+0

是的,它的工作!但是,我有另一个问题,我不明白OPENQUERY和OPENROWSET函数之间的区别,两条指令都从查询中检索行。对不起'Noob'的问题,但是,我对SQL很不好。 –

+0

我知道的唯一真正的区别是'OPENQUERY'要求存在一个链接的服务器,'OPENROWSET'要求你指定所有其他的连接参数。文档位于:http://msdn.microsoft.com/en-us/library/ms188427.aspx http://msdn.microsoft。COM/EN-US /库/ ms190312.aspx –

1

由于全局临时表故障,使用一个真正的表,当您启动脚本创建和删除临时表,一旦你做是为了确保运行此。

IF OBJECT_ID('dbo.temptable', 'U') IS NOT NULL 
BEGIN 
DROP TABLE dbo.temptable 
END 

CREATE TABLE dbo.temptable 
( ...) 
+0

该op说他们已经尝试过全球临时表。 –

+0

@AaronBertrand谢谢,我错过了。改变了我的答案。因为Op在他的例子中只使用了1#,所以令人困惑。 –

+1

好吧,这会导致并发性...如果两个用户同时运行该过程,会发生什么情况?你需要使用sp_getapplock或其他信号量机制来确保整个过程得到保护......当使用环回连接的服务器时,这可能会非常棘手,因此可能是分布式事务... –

0

您需要在同一连接内运行两个查询并使用全局临时表。

+1

是的,全球临时表总是*听起来像一个好主意。直到两个用户同时调用存储过程。 –

0

在SQL Server 2008中,您可以声明代表表结构定义的User-Defined Table Types。创建完成后,您可以在您的proc中创建表参数并将其传递很长时间,并且可以访问其他procs中的表。

0

我猜这种行为的原因是,当你从另一个服务器调用OPENROWSET时,它首先分别请求有关过程输出结构(METADATA)的信息。最有趣的是这个输出结构是从过程中的第一个SELECT语句中获取的。而且,如果SELECT语句遵循IF条件,则METADATA请求会忽略此IF条件,因为不需要运行整个过程 - 第一个满足的SELECT语句就足够了。 (顺便说一下,要关闭该行为,可以在程序开始时包含SET FMTONLY OFF,但这可能会增加过程执行时间)。

结论:

- 当被请求的元数据是从一个临时表(在过程中创建的)实际上并不存在,因为元数据请求不实际运行过程并创建临时表。

- 如果它是至关重要的业务使用临时表中,元数据请求可以用假第一 SELECT语句喂 - 如果临时表中可以用它解决了这个问题

表变量来代替,如:

declare @t table(ID int, Name varchar(15)); 
if (0 = 1) select ID, Name from @t;   -- fake SELECT statement 
create table #T (ID int, Name varchar(15)); 
select ID, Name from #T;     -- real SELECT statement 

- 还有一件事是使用具有FMTONLY一个常用的伎俩(这不是我的想法):

declare @fmtonlyOn bit = 0; 
if 1 = 0 set @fmtonlyOn = 1; 
set fmtonly off; 
create table #T (ID int, Name varchar(15));  
if @fmtonlyOn = 1 set fmtonly on; 
select ID, Name from #T; 
0

您收到错误的原因是因为临时表#Client在运行插入到该过程之前未被声明。如果你申报表,然后执行列表proc和使用直接插入 -

INSERT INTO #Client

EXEC LIST_CLIENTS