从SP和临时表中选择值

时间:2011-08-01 19:55:50

标签: sql sql-server tsql sql-server-2008

我在MSSQL 2008中有一个存储过程,在此我创建了一个临时表,然后我在临时表中执行了几次插入。 如何选择存储过程外的临时表的所有列?我的意思是,我有这个:

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的范围?

提前致谢

6 个答案:

答案 0 :(得分:1)

由于全局临时表失败,请使用实际表,在启动创建脚本时运行此表,并在完成后删除临时表以确保。

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

CREATE TABLE dbo.temptable 
(    ... ) 

答案 1 :(得分: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表没有被正确引用。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

在SQL Server 2008中,您可以声明表示表结构定义的User-Defined Table Types。创建后,您可以在procs中创建表参数并将它们传递一段时间,并能够访问其他过程中的表。

答案 4 :(得分:0)

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

结论:

- 当从临时表(在过程中创建)中请求METADATA时,它实际上不存在,因为METADATA请求实际上并不运行该过程并创建临时表。

- 如果可以用表变量替换临时表,则可以解决问题

- 如果企业使用临时表至关重要,那么METADATA请求可以使用伪第一个 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;

答案 5 :(得分:0)

您收到错误的原因是因为在您运行插入过程的过程之前未声明临时表#Client。如果声明表,则执行list proc并使用直接插入 -

INSERT INTO #Client

EXEC LIST_CLIENTS