【问题标题】:Select Values From SP And Temporary Tables从 SP 和临时表中选择值
【发布时间】:2011-08-01 19:55:49
【问题描述】:

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

提前致谢

【问题讨论】:

  • 能否请您用您正在使用的 SQL Server 版本标记问题?另外,如果您使用的是受信任的连接,为什么要传递 uid 和 pwd?
  • 你知道全局临时表是用##声明的吗?

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


【解决方案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 表没有被正确引用。

【讨论】:

  • 是的,成功了!但是,我还有一个问题,我不明白 OPENQUERY 和 OPENROWSET 函数之间的区别,两条指令都从查询中检索行。对于“菜鸟”的问题,我很抱歉,但是,我对 SQL 真的很不好。
  • 我所知道的唯一真正区别是OPENQUERY 要求存在链接服务器,而OPENROWSET 要求您指定所有其他连接参数。文档在这里:msdn.microsoft.com/en-us/library/ms188427.aspxmsdn.microsoft.com/en-us/library/ms190312.aspx
【解决方案2】:

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

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

CREATE TABLE dbo.temptable 
(    ... ) 

【讨论】:

  • @AaronBertrand 谢谢,我错过了。改变了我的答案。令人困惑,因为 Op 在他的示例中只使用了 1 #。
  • 好吧,这会扼杀并发性......如果两个用户同时运行该过程会发生什么?您需要使用 sp_getapplock 或其他一些信号量机制来确保整个进程受到保护......这在使用环回链接服务器时显然会变得很棘手,因此可能会使用分布式事务......
  • @AaronBertrand 你又是对的,我想他应该制作一个永久表,每个用户都有自己的行(包括表中的用户 ID),然后在他完成后删除这些行并且一旦他开始创建表的过程。
  • 我怀疑答案比这更简单——例如,@table 变量可以完全避免这个问题。仍在试验中,还不能重现问题。
  • @AaronBertrand 让我们continue this discussion in chat
【解决方案3】:

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

【讨论】:

  • 是的,全局临时表总是听起来是个好主意。直到两个用户同时调用存储过程。
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

    结论:

    ——当从临时表(在过程中创建)请求元数据时,它实际上并不存在,因为元数据请求实际上并没有运行过程并创建临时表。

    ——如果一个临时表可以用一个表变量来代替它就解决了这个问题

    ——如果使用临时表对业务至关重要,则可以使用虚假的 first SELECT 语句来提供 METADATA 请求,例如:

    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;
    

    【讨论】:

      【解决方案6】:

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

      插入#Client

      EXEC LIST_CLIENTS

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-03
        • 1970-01-01
        • 1970-01-01
        • 2019-02-09
        相关资源
        最近更新 更多