如何将存储过程中的宽结果集插入到临时 table 或 table 变量中?

How to insert wide result set from stored procedure into a temporary table or table variable?

我正在尝试使用我发现的各种方法将存储过程的输出保存到临时 table 或 table 变量中。

我试过声明一个临时 table 然后做:

INSERT INTO #temptable EXEC storedprocedurename 

我 运行 遇到了一个问题,因为存储过程在结果集中有大约 550 列。我无法创建那么宽的临时 table,因为最大行大小超过 8060 字节。

不幸的是,我没有编辑存储过程的能力,但我真的只需要访问这些列中的大约 200 个。有没有办法只获取我想要的列并将它们粘贴在 table 变量或临时 table 中以绕过大小问题,或者我是否将整行结果粘贴在 table?

我也调查了 OPENROWSET,但我使用的 SQL 服务器不允许启用 ADHOC 查询,也不会启用。在这一点上我没有想法,因为我唯一知道要做的就是克隆存储过程并删除大约 1/2 我不需要的列,但是如果源代码,这可能是一个维护问题存储过程不断变化,我对此一无所知。

您可以使用游标将数据发送到 temp table,并限制 cols 的数量,直到它到达末尾。这可能会影响性能。

我找不到在 INSERT ... EXEC 语句中只插入部分列的方法。这意味着 temp table 应该包含所有 550 列。但是,您可以尝试避免将所有返回的数据插入到这些列中。

第一个选项。 INSTEAD OF 触发器。我从来没有用过,所以我不确定它是否适用于 temp tables。它应该与正常的 tables 一起工作。主要思想 - 拦截触发器中的 INSERT 操作,并用 NULLs.

替换超过 8060 字节限制且不需要的 long 值

第二个选项。对于那些不需要的长 varchar 列,在 CREATE temp table 语句中将它们定义为 varchar(1)。主要思想 - 将不需要的长 varchar 值截断为 1 个字符,希望最后一行小于 8060 字节。如果您尝试这种方法,您很可能会看到此错误消息:String or binary data would be truncated. 要抑制此错误消息并强制服务器截断数据:SET ansi_warnings OFF.

您是否尝试过 OPENQUERY? I do not believe it has the same requirements as OPENROWSET. You would create a "loopback" Linked Server and use that in the call to OPENQUERY. There are some restrictions on OPENQUERY, though, in that it does not like Dynamic SQL or anything else that is a restriction of sp_describe_first_result_set,它在内部使用。

EXEC sp_addlinkedserver
   @server = N'Loopback',
   @srvproduct = N'',
   @provider = N'SQLNCLI',
   @datasrc = N'<your server name>';

然后尝试:

SELECT * FROM OPENQUERY(Loopback, N'EXEC sp_who2'); -- this gets an error

SELECT * FROM OPENQUERY(Loopback, N'EXEC sp_who'); -- this works

如果 OPENQUERY 不是一个选项,那么这仍然可以通过 SQLCLR 轻松完成。您有两个主要选择:

  1. SQLCLR 存储过程: 这是更简单的方法,因为没有与现有存储过程相关的限制(即动态 SQL、副作用函数、DML、SET 语句等)。您可以使用内部上下文连接(即附加到您已经在的同一会话,这在程序集标记为 SAFE 时有效),执行存储过程,并使用 [= 循环显示结果18=],使用 SqlContext.Pipe.SendResultsRow 将每一行发送回调用者(即将行流回)。您将使用 SqlDataRecord 创建一个结果集结构,并且只创建您想要的列。然后,您从 SqlDataReader 设置这些列中的每一个,SqlDataReader 有许多从未访问过的列并不重要。

  2. SQLCLR Table-Valued Function: 这种方法的好处是实际上能够发出一个 SELECT 反对操作,以便您可以添加 WHERE 条件等。您仍然会使用 SqlCommand / SqlDataReader,就像 SQLCLR 存储过程一样。但是,有一些限制,就像 T-SQL 函数一样。

    • 如果您当前的存储过程没有违反任何创建 T-SQL 函数的规则(即动态 SQL、副作用函数、DML、SET 语句等),然后您可以在使用内部上下文连接时从 SQLCLR 函数只读执行存储过程(即附加到您已经在的同一个会话,这在程序集被标记时有效作为 SAFE )。但是,您将无法将行流回,而是需要将它们存储在内存中(类似于在 T-SQL 多语句 TVF 中使用 Table 变量)并释放过程结束时的结果。
    • 如果您当前的存储过程确实违反了这些规则中的任何一条(并且执行 SET NOCOUNT ON; 将是违反的,因为它是 SET 语句),那么您仍然可以这样做,但是您会需要使用常规/外部连接,因为上下文连接不起作用。此选项确实具有能够使用 yield return 流回行的好处,但它也有两个缺点:不是同一会话的一部分(因此无法看到本地临时表或CONTEXT_INFO),并要求将程序集标记为 EXTERNAL_ACCESS(但这 要求将数据库设置为 TRUSTWORTHY ON:您只需签署程序集,从该 DLL / 程序集在 master 中创建一个非对称密钥,从该非对称密钥创建一个登录名,最后授予该登录名 EXTERNAL ACCESS ASSEMBLY 权限)。

并且看到您正在访问的 SQL 服务器不允许 Ad Hoc Distributed Queries,如果 DBA 说 SQLCLR 也由于 [=70 而无法启用=] 风险,那么请将他们的注意力转移到我在 SQL Server Central 上写的关于 SQLCLR 的一系列文章:Stairway to SQLCLR(需要免费注册才能查看该站点上的内容)。在前 4 篇文章中,我通过几个示例展示了 SQLCLR 的安全性,尤其是当程序集被标记为 SAFE(涵盖上述 3 个选项中的 2 个)时。